package apollo

import (
	"database/sql"
	"fmt"
	"math"
	"strconv"
	"time"

	"github.com/cheggaaa/pb"
	log "github.com/sirupsen/logrus"

	hdmap "camap/modules/map/proto"

	_ "github.com/shaxbee/go-spatialite"
	"github.com/twpayne/go-geom"
	"github.com/twpayne/go-geom/encoding/wkb"
)

var db *sql.DB
var xyz bool

func SetDB(in *sql.DB) {
	db = in
}
func SetXYZ(z bool) {
	xyz = z
}

type LaneHelper struct {
	Pid       string
	Link      string
	Start     string
	End       string
	Seq       int
	Width     float64
	Direction int
	Turn      int
	Type      int32
	Speed     float64
	Length    float64
}
type LLB struct {
	Link     string
	Lane     string
	Boundary string
	LSeq     int
	BSeq     int
	Az       float64
}
type LaneNode struct {
	Start string
	End   string
}

type PtVector struct {
	X, Y float64
}

type Point3D struct {
	X, Y, Z float64
}

type CurveLine []*Point3D

type Poly3C struct {
	Au, Bu, Cu, Du float64
	Av, Bv, Cv, Dv float64
	Length         float64
}

func MakeAllOverlaps() []*hdmap.Overlap {
	ols := []*hdmap.Overlap{}
	//make lane&signal overlaps
	st := time.Now()
	olSignals := MakeAllLaneSignalOverlaps()
	ols = append(ols, olSignals...)
	log.Infof("MakeAllOverlaps..... make all %d LaneSignalOverlaps, time cost:%fs ~\n", len(olSignals), time.Since(st).Seconds())
	//make lane&crosswalk overlaps
	st = time.Now()
	olCws := MakeAllLaneCwOverlaps()
	ols = append(ols, olCws...)
	log.Infof("MakeAllOverlaps..... make all %d LaneCwOverlaps, time cost:%fs ~\n", len(olCws), time.Since(st).Seconds())
	//make junction&lane
	st = time.Now()
	olJunLanes := MakeAllJunctionLaneOverlaps()
	ols = append(ols, olJunLanes...)
	log.Infof("MakeAllOverlaps..... make all %d JunctionLaneOverlaps, time cost:%fs ~\n", len(olJunLanes), time.Since(st).Seconds())
	//make junction&crosswalk
	st = time.Now()
	olJunCws := MakeAllJunctionCwOverlaps()
	ols = append(ols, olJunCws...)
	log.Infof("MakeAllOverlaps..... make all %d JunctionCwOverlaps, time cost:%fs ~\n", len(olJunCws), time.Since(st).Seconds())
	//make lane&lane
	st = time.Now()
	olLaneLanes := MakeAllLaneLaneOverlaps()
	ols = append(ols, olLaneLanes...)
	log.Infof("MakeAllOverlaps..... make all %d LaneLaneOverlaps, time cost:%fs ~\n", len(olLaneLanes), time.Since(st).Seconds())
	return ols
}

func MakeAllLaneLaneOverlaps() []*hdmap.Overlap {
	st := time.Now()
	// make lanelane overlaps:
	ols := []*hdmap.Overlap{}
	//get all lanepids
	lpids := GetAllOlLanes()
	i := 0
	total := len(lpids)
	bar := pb.StartNew(total)
	deDup := make(map[string]*hdmap.Overlap)
	for k, v := range lpids {
		// st := time.Now()
		if v.Start != "" {
			starts := MakeLaneLaneStarts(k, v.Start)
			for _, s := range starts {
				id := s.Id.String()
				deDup[id] = s
			}
		}
		if v.End != "" {
			ends := MakeLaneLaneEnds(k, v.End)
			for _, e := range ends {
				id := e.Id.String()
				deDup[id] = e
			}
		}
		// fmt.Printf("progress(%d/%d),make lane(pid:%s)&lanes overlap, time cost: %fs ~\n", i+1, total, k, time.Since(st).Seconds())
		bar.Increment()
		i++
	}

	for _, v := range deDup {
		ols = append(ols, v)
	}

	bar.Finish()
	fmt.Printf("make all %d lane&lane overlaps, time cost:%fs ~\n", total, time.Since(st).Seconds())
	return ols
}

func MakeLaneLaneStarts(lpid, start string) []*hdmap.Overlap {
	// make lanelane overlaps:
	ols := []*hdmap.Overlap{}
	rows, err := db.Query(`SELECT lane_link_pid
	FROM ibd_lane_link
	WHERE lane_link_pid != ?
		AND s_lane_node_pid = ?;`, lpid, start)
	if err != nil {
		log.Errorf("get start overlap lanes error, detail:%v", err)
		return ols
	}
	defer rows.Close()
	l2pids := []string{}
	for rows.Next() {
		var l2pid string
		err := rows.Scan(&l2pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		l2pids = append(l2pids, l2pid)
	}

	for _, l2pid := range l2pids {
		//lane&lane
		ol := MakeLaneLaneOverlap(lpid, l2pid)
		ols = append(ols, ol)
	}

	return ols
}

func MakeLaneLaneEnds(lpid, end string) []*hdmap.Overlap {
	// make lanelane overlaps:
	ols := []*hdmap.Overlap{}
	rows, err := db.Query(`SELECT lane_link_pid
	FROM ibd_lane_link
	WHERE lane_link_pid != ?
		AND e_lane_node_pid = ?;`, lpid, end)
	if err != nil {
		log.Errorf("get end overlap lanes error, detail:%v", err)
		return ols
	}
	defer rows.Close()
	l2pids := []string{}
	for rows.Next() {
		var l2pid string
		err := rows.Scan(&l2pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		l2pids = append(l2pids, l2pid)
	}

	for _, l2pid := range l2pids {
		//lane&lane
		olPoly := MakeLaneLanePolygon(lpid, l2pid)
		olId := MakeId("Lane" + lpid + "_Lane" + l2pid)
		regionId := MakeId("Region_L" + lpid + "_L" + l2pid)
		if l2pid < lpid { //确保小的在前
			olId = MakeId("Lane" + l2pid + "_Lane" + lpid)
			regionId = MakeId("Region_L" + l2pid + "_L" + lpid)
		}
		start1, start2 := 0.0, 0.0
		end1, end2 := 0.0, 0.0
		info1 := hdmap.ObjectOverlapInfo{
			Id: MakeId(lpid),
			OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
				LaneOverlapInfo: &hdmap.LaneOverlapInfo{
					StartS:          &start1,
					EndS:            &end1,
					RegionOverlapId: regionId,
				},
			},
		}
		info2 := hdmap.ObjectOverlapInfo{
			Id: MakeId(l2pid),
			OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
				LaneOverlapInfo: &hdmap.LaneOverlapInfo{
					StartS:          &start2,
					EndS:            &end2,
					RegionOverlapId: regionId,
				},
			},
		}

		infos := []*hdmap.ObjectOverlapInfo{&info1, &info2}

		regionInfo := &hdmap.RegionOverlapInfo{
			Id:      regionId,
			Polygon: []*hdmap.Polygon{olPoly},
		}

		ol := &hdmap.Overlap{
			Id:            olId,
			Object:        infos,
			RegionOverlap: []*hdmap.RegionOverlapInfo{regionInfo},
		}
		ols = append(ols, ol)
	}

	return ols
}

func GetAllOlLanes() map[string]*LaneNode {
	//get all lanepids
	lpids := make(map[string]*LaneNode) //TODO:

	rows, err := db.Query(`SELECT T.lane_link_pid,T.s_lane_node_pid
							FROM ibd_lane_link T
								INNER JOIN (
									SELECT s_lane_node_pid,
										COUNT (*) cnt
									FROM ibd_lane_link
									GROUP BY s_lane_node_pid
								) AS b ON b.s_lane_node_pid = T.s_lane_node_pid
							WHERE b.cnt > 1;`)
	if err != nil {
		log.Errorf("获取所有startnode共点车道参考线失败, details: %v", err)
		goto endnode
	}
	//只处理前两个
	for rows.Next() {
		var lpid, start string
		err := rows.Scan(&lpid, &start)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		lpids[lpid] = &LaneNode{
			Start: start,
		}
	}

endnode:
	rows, err = db.Query(`SELECT T.lane_link_pid pid,
								T.e_lane_node_pid e
							FROM ibd_lane_link T
								INNER JOIN (
									SELECT e_lane_node_pid,
										COUNT (*) cnt
									FROM ibd_lane_link
									GROUP BY e_lane_node_pid
								) AS b ON b.e_lane_node_pid = T.e_lane_node_pid
							WHERE b.cnt > 1;`)
	if err != nil {
		log.Errorf("获取所有endnode共点的车道参考线失败, details: %v", err)
		return lpids
	}
	//只处理前两个
	for rows.Next() {
		var lpid, end string
		err := rows.Scan(&lpid, &end)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ln, ok := lpids[lpid]
		if !ok {
			lpids[lpid] = &LaneNode{
				End: end,
			}
		} else {
			ln.End = end
		}
	}
	return lpids
}

func MakeAllJunctionCwOverlaps() []*hdmap.Overlap {
	ols := []*hdmap.Overlap{}
	rows, err := db.Query(`select object_pid from ibd_object_cross;`)
	if err != nil {
		log.Errorf("get all junctions error, detail:%v", err)
		return ols
	}
	defer rows.Close()
	jpids := []string{}
	for rows.Next() {
		var jpid string
		err := rows.Scan(&jpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		jpids = append(jpids, jpid)
	}
	// st := time.Now()
	// total := len(jpids)
	for _, jpid := range jpids {
		//junction&crosswalk
		// st := time.Now()
		rows, err = db.Query(`select w.object_pid,ST_AsBinary(ST_Intersection(w.geometry,c.geometry)) from ibd_object_cross c, ibd_object_cross_walk w
		where contains(c.geometry,w.geometry) = 1 and c.object_pid = ?;`, jpid)
		if err != nil {
			log.Errorf("get junction&crosswalk overlaps error, details:%s", err)
			continue
		}
		for rows.Next() {
			var wpid string
			var poly wkb.Polygon
			err := rows.Scan(&wpid, &poly)
			if err != nil {
				log.Errorf("rows scan error, details:%s", err)
				continue
			}
			olid := MakeId("Junction" + jpid + "_CrossWalk" + wpid)
			regionId := MakeId("Region_J" + jpid + "_CW" + wpid)
			info1 := hdmap.ObjectOverlapInfo{
				Id:          MakeId(jpid),
				OverlapInfo: &hdmap.ObjectOverlapInfo_JunctionOverlapInfo{},
			}
			info2 := hdmap.ObjectOverlapInfo{
				Id: MakeId(wpid),
				OverlapInfo: &hdmap.ObjectOverlapInfo_CrosswalkOverlapInfo{
					CrosswalkOverlapInfo: &hdmap.CrosswalkOverlapInfo{
						RegionOverlapId: regionId,
					},
				},
			}

			infos := []*hdmap.ObjectOverlapInfo{&info1, &info2}

			points := []*hdmap.PointENU{}
			if poly.Layout().ZIndex() != -1 {
				flat := poly.FlatCoords()
				for i := 0; i < len(flat); i = i + poly.Stride() {
					x, y, z := flat[i], flat[i+1], flat[i+2]
					if !xyz {
						z = 0.0
					}
					points = append(points, &hdmap.PointENU{
						X: &x,
						Y: &y,
						Z: &z,
					})
				}
			}
			regionPoly := &hdmap.Polygon{
				Point: points,
			}

			regionInfo := &hdmap.RegionOverlapInfo{
				Id:      regionId,
				Polygon: []*hdmap.Polygon{regionPoly},
			}
			ol := &hdmap.Overlap{
				Id:            olid,
				Object:        infos,
				RegionOverlap: []*hdmap.RegionOverlapInfo{regionInfo},
			}
			ols = append(ols, ol)
		}
		// fmt.Printf("progress(%d/%d),make junction(pid:%s)&cws overlap, time cost: %fs ~\n", i+1, total, jpid, time.Since(st).Seconds())
	}
	// fmt.Printf("make all %d junction&cws overlaps, time cost:%fs ~\n", total, time.Since(st).Seconds())

	return ols
}

func MakeAllJunctionLaneOverlaps() []*hdmap.Overlap {
	//junction&lane overlaps, 当前所有lane都在junction内部
	ols := []*hdmap.Overlap{}
	rows, err := db.Query(`select object_pid from ibd_object_cross;`)
	if err != nil {
		log.Errorf("get all junctions error, detail:%v", err)
		return ols
	}
	defer rows.Close()
	jpids := []string{}
	for rows.Next() {
		var jpid string
		err := rows.Scan(&jpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		jpids = append(jpids, jpid)
	}
	st := time.Now()
	total := len(jpids)
	for _, jpid := range jpids {
		// st := time.Now()
		//junction&lane
		rows, err := db.Query(`select l.lane_link_pid,st_length(l.geometry) from ibd_object_cross c, ibd_lane_link l
		where contains(c.geometry,l.geometry) = 1 and c.object_pid = ?;`, jpid)
		if err != nil {
			log.Errorf("get junction&lane overlaps error, details:%s", err)
			continue
		}
		num := 1
		for rows.Next() {
			// st := time.Now()
			var lpid string
			var length float64
			err := rows.Scan(&lpid, &length)
			if err != nil {
				log.Errorf("rows scan error, details:%s", err)
				continue
			}
			s := 0.0
			olid := MakeId("Junction" + jpid + "_Lane" + lpid)
			regionId := MakeId("Region_J" + jpid + "_L" + lpid)
			info1 := hdmap.ObjectOverlapInfo{
				Id:          MakeId(jpid),
				OverlapInfo: &hdmap.ObjectOverlapInfo_JunctionOverlapInfo{},
			}
			info2 := hdmap.ObjectOverlapInfo{
				Id: MakeId(lpid),
				OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
					LaneOverlapInfo: &hdmap.LaneOverlapInfo{
						StartS:          &s,
						EndS:            &length,
						RegionOverlapId: regionId,
					},
				},
			}
			infos := []*hdmap.ObjectOverlapInfo{&info1, &info2}

			laneRegion := MakeLanePolygon(lpid)
			regionInfo := &hdmap.RegionOverlapInfo{
				Id: regionId,
				Polygon: []*hdmap.Polygon{
					laneRegion,
				},
			}
			ol := &hdmap.Overlap{
				Id:            olid,
				Object:        infos,
				RegionOverlap: []*hdmap.RegionOverlapInfo{regionInfo},
			}
			ols = append(ols, ol)
			// fmt.Printf("make junction(pid:%s)& %dth lane(pid:%s) overlap, time cost: %fs ~\n", jpid, num, lpid, time.Since(st).Seconds())
			num++
		}
		// fmt.Printf("progress(%d/%d),make junction(pid:%s)&lanes overlap, time cost: %fs ~\n", i+1, total, jpid, time.Since(st).Seconds())
	}
	fmt.Printf("make all %d junction&lanes overlaps, time cost:%fs ~\n", total, time.Since(st).Seconds())
	return ols
}

func MakeAllLaneSignalOverlaps() []*hdmap.Overlap {
	ols := []*hdmap.Overlap{}
	//lane&signal overlaps
	opids := []string{}
	lpids := []string{}
	rows, err := db.Query(`select object_pid, lane_link_pid from ibd_object_lane_link_rel where object_type = 17;`)
	if err != nil {
		log.Errorf("get all overlaps error, detail:%s", err)
		return ols
	}

	for rows.Next() {
		var opid, lpid string
		err := rows.Scan(&opid, &lpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		opids = append(opids, opid)
		lpids = append(lpids, lpid)
	}
	//lane overlaps infos
	st := time.Now()
	total := len(opids)
	for i := 0; i < total; i++ {
		st = time.Now()
		//信号灯暂不去计算该灯或者停止线的start&end
		//GetLaneSignalStartEnd
		s, e := CaclLaneSignalStartEnd(lpids[i], opids[i])
		info1 := hdmap.ObjectOverlapInfo{
			Id: MakeId(lpids[i]),
			OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
				LaneOverlapInfo: &hdmap.LaneOverlapInfo{
					StartS: &s,
					EndS:   &e,
				},
			},
		}
		info2 := hdmap.ObjectOverlapInfo{
			Id:          MakeId(opids[i]),
			OverlapInfo: &hdmap.ObjectOverlapInfo_SignalOverlapInfo{},
		}

		ol := &hdmap.Overlap{
			Id:     MakeId("Lane" + lpids[i] + "_Signal" + opids[i]),
			Object: []*hdmap.ObjectOverlapInfo{&info1, &info2},
		}
		ols = append(ols, ol)
		// fmt.Printf("progress(%d/%d),make lane(pid:%s)&signal(pid:%s) overlap, time cost: %fs ~\n", i+1, total, lpids[i], opids[i], time.Since(st).Seconds())
	}
	fmt.Printf("make all %d lane&signal overlaps, time cost:%fs ~\n", total, time.Since(st).Seconds())
	return ols
}

func CaclLaneSignalStartEnd(lanepid, signalpid string) (startS, endS float64) {
	//start,end
	startS, endS = 0.0, 0.1
	rs := db.QueryRow(`SELECT ST_InterpolatePoint (l.geom, INTERSECTION (sl.geometry, l.geom))
	FROM aodx_lane_boundary l,
		(
			SELECT *
			FROM ibd_object_stopline
			WHERE object_pid IN (
					SELECT object_pid
					FROM ibd_object_combinant
					WHERE combinant_pid IN (
							SELECT combinant_pid
							FROM ibd_object_combinant
							WHERE object_pid = ?
						)
						AND object_type = 7
				)
		) sl
	WHERE l.lane_link_pid = ?;`, signalpid, lanepid)

	var se sql.NullFloat64
	err := rs.Scan(&se)
	if err != nil {
		if err != sql.ErrNoRows {
			log.Errorf("cacl lane(pid:%s) signal(pid:%s) start end error, details:%v", lanepid, signalpid, err)
		}
		return
	}
	if se.Valid {
		startS = se.Float64
		endS = startS + 0.1
	}
	return startS, endS
}

func MakeAllLaneCwOverlaps() []*hdmap.Overlap {
	ols := []*hdmap.Overlap{}
	//lane&signal overlaps
	opids := []string{}
	lpids := []string{}
	rows, err := db.Query(`select object_pid, lane_link_pid from ibd_object_lane_link_rel where object_type = 18;`)
	if err != nil {
		log.Errorf("get all overlaps error, detail:%s", err)
		return ols
	}

	for rows.Next() {
		var opid, lpid string
		err := rows.Scan(&opid, &lpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		opids = append(opids, opid)
		lpids = append(lpids, lpid)
	}
	st := time.Now()
	total := len(opids)
	//lane overlaps infos
	for i := 0; i < total; i++ {
		st = time.Now()
		s, e := CaclLaneCwStartEnd(lpids[i], opids[i])
		regionid := MakeId("Region_L" + lpids[i] + "_CW" + opids[i])
		info1 := hdmap.ObjectOverlapInfo{
			Id: MakeId(lpids[i]),
			OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
				LaneOverlapInfo: &hdmap.LaneOverlapInfo{
					StartS:          &s,
					EndS:            &e,
					RegionOverlapId: regionid,
				},
			},
		}
		info2 := hdmap.ObjectOverlapInfo{
			Id: MakeId(opids[i]),
			OverlapInfo: &hdmap.ObjectOverlapInfo_CrosswalkOverlapInfo{
				CrosswalkOverlapInfo: &hdmap.CrosswalkOverlapInfo{
					RegionOverlapId: regionid,
				},
			},
		}
		poly := CaclLaneCwRegion(lpids[i], opids[i])
		regOverlap := &hdmap.RegionOverlapInfo{
			Id:      regionid,
			Polygon: []*hdmap.Polygon{poly},
		}
		ol := &hdmap.Overlap{
			Id:            MakeId("Lane" + lpids[i] + "_CrossWalk" + opids[i]),
			Object:        []*hdmap.ObjectOverlapInfo{&info1, &info2},
			RegionOverlap: []*hdmap.RegionOverlapInfo{regOverlap},
		}

		ols = append(ols, ol)
		// fmt.Printf("progress(%d/%d),make lane(pid:%s)&cw(pid:%s) overlap, time cost: %fs ~\n", i+1, total, lpids[i], opids[i], time.Since(st).Seconds())
	}
	fmt.Printf("make all %d laneCwOverlaps, time cost: %fs ~\n", total, time.Since(st).Seconds())
	return ols
}

func CaclLaneCwStartEnd(lanepid, cwpid string) (startS, endS float64) {
	//start,end
	rs := db.QueryRow(`select 
		ST_InterpolatePoint(ST_AddMeasure(t.lg,0,ST_Length(t.lg)), startpoint(t.ng)) as s,
		ST_InterpolatePoint(ST_AddMeasure(t.lg,0,ST_Length(t.lg)), endpoint(t.ng)) as e
	from (
        select intersection(l.geometry,cw.geometry) as ng,
            l.geometry as lg
        from ibd_lane_link l,
            ibd_object_cross_walk cw
        where l.lane_link_pid = ?
            and cw.object_pid = ?
    ) t;`, lanepid, cwpid)

	var s, e sql.NullFloat64
	err := rs.Scan(&s, &e)
	if err != nil {
		log.Error(err)
	}
	if s.Valid {
		startS = s.Float64
	}
	if e.Valid {
		endS = e.Float64
	}
	return startS, endS
}

func CaclLaneCwRegion(lanepid, cwpid string) (poly *hdmap.Polygon) {

	rs := db.QueryRow(`select ST_AsBinary(ST_Transform(SetSRID(intersection(cw.geometry, p.g),4549),32650)) as ng
	from (
			select ST_MakePolygon(
					LineMerge(
						Collect(
							addpoint(l.bgeom, endpoint(r.bgeom)),
							addpoint(r.bgeom, startpoint(l.bgeom), 0)
						)
					)
				) as g
			from (
					select bgeom
					from aodx_lane_boundary
					where lane_link_pid = ?
					order by bseq
					limit 1
				) l, (
					select bgeom
					from aodx_lane_boundary
					where lane_link_pid = ?
					order by bseq
					limit 1, 1
				) r
		) p, ibd_object_cross_walk cw
	where cw.object_pid = ?;`, lanepid, lanepid, cwpid)

	var pg wkb.Polygon
	err := rs.Scan(&pg)
	if err != nil {
		log.Error(err)
		return &hdmap.Polygon{}
	}

	points := []*hdmap.PointENU{}
	if pg.Layout().ZIndex() != -1 {
		flat := pg.FlatCoords()
		for i := 0; i < len(flat); i = i + 3 {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}

	return &hdmap.Polygon{
		Point: points,
	}
}

func MakeLanePolygon(lanepid string) (poly *hdmap.Polygon) {
	//构造laneregion
	rs := db.QueryRow(`select ST_AsBinary(ST_Transform(SetSRID(ST_MakePolygon(
								LineMerge(
									Collect(
										addpoint(l.bgeom, endpoint(r.bgeom)),
										addpoint(r.bgeom, startpoint(l.bgeom), 0)
									)
								)
							),4549),32650)) as g
					    from (
								select bgeom
								from aodx_lane_boundary
								where lane_link_pid = ?
								order by bseq
								limit 1
							) l, (
								select bgeom
								from aodx_lane_boundary
								where lane_link_pid = ?
								order by bseq
								limit 1, 1
							) r;`, lanepid, lanepid)

	var pg wkb.Polygon
	err := rs.Scan(&pg)
	if err != nil {
		if err != sql.ErrNoRows {
			log.Error("make lane(pid:) polygon error, details:%v", lanepid, err)
		}
		return &hdmap.Polygon{}
	}

	points := []*hdmap.PointENU{}
	if pg.Layout().ZIndex() != -1 {
		stride := pg.Layout().Stride()
		flat := pg.FlatCoords()
		for i := 0; i < len(flat); i = i + stride {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}

	return &hdmap.Polygon{
		Point: points,
	}
}

func MakeLaneLanePolygon(l1pid, l2pid string) (poly *hdmap.Polygon) {
	if IsLaneInJunction(l1pid) {
		return &hdmap.Polygon{}
	}
	//构造laneregion
	rs := db.QueryRow(`SELECT ST_AsBinary(ST_Transform(SetSRID(ST_Intersection(p2.G,p1.G),4549),32650))
	FROM (
			SELECT ST_MakePolygon (
					LineMerge (
						COLLECT (
							addpoint (l.bgeom, endpoint (r.bgeom)),
							addpoint (r.bgeom, startpoint (l.bgeom), 0)
						)
					)
				) AS G
			FROM (
					SELECT bgeom
					FROM aodx_lane_boundary
					WHERE lane_link_pid = ?
					ORDER BY bseq
					LIMIT 1
				) l, (
					SELECT bgeom
					FROM aodx_lane_boundary
					WHERE lane_link_pid = ?
					ORDER BY bseq
					LIMIT 1, 2
				) r
		) p2, (
			SELECT ST_MakePolygon (
					LineMerge (
						COLLECT (
							addpoint (l.bgeom, endpoint (r.bgeom)),
							addpoint (r.bgeom, startpoint (l.bgeom), 0)
						)
					)
				) AS G
			FROM (
					SELECT bgeom
					FROM aodx_lane_boundary
					WHERE lane_link_pid = ?
					ORDER BY bseq
					LIMIT 1
				) l, (
					SELECT bgeom
					FROM aodx_lane_boundary
					WHERE lane_link_pid = ?
					ORDER BY bseq
					LIMIT 1, 2
				) r
		) p1;`, l1pid, l1pid, l2pid, l2pid)

	var pg wkb.Polygon //可能是multipolygon，nil，point，collection，mutiline等等
	err := rs.Scan(&pg)
	if err != nil {
		if err != sql.ErrNoRows {
			log.Errorf("构造lanelaneregion出错,l1pid:%s,l2pid:%s, 详情：%v", l1pid, l2pid, err)
		}
		return &hdmap.Polygon{}
	}

	points := []*hdmap.PointENU{}
	if pg.Layout().ZIndex() != -1 {
		stride := pg.Layout().Stride()
		flat := pg.FlatCoords()
		for i := 0; i < len(flat); i = i + stride {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}
	return &hdmap.Polygon{
		Point: points,
	}
}

func MakeLaneLaneOverlap(l1pid, l2pid string) (overlap *hdmap.Overlap) {
	//构造laneregion
	olPoly := MakeLaneLanePolygon(l1pid, l2pid)

	minx, miny, maxx, maxy := math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64
	for _, pt := range olPoly.Point {
		if *pt.X < minx {
			minx = *pt.X
		}
		if *pt.Y < miny {
			miny = *pt.Y
		}
		if *pt.X > maxx {
			maxx = *pt.X
		}
		if *pt.Y > maxy {
			maxy = *pt.Y
		}
	}
	start1, end1, start2, end2 := CaclLaneStartEnd(l1pid, l2pid)

	olId := MakeId("Lane" + l1pid + "_Lane" + l2pid)
	regionId := MakeId("Region_L" + l1pid + "_L" + l2pid)
	if l2pid < l1pid { //确保小的在前
		olId = MakeId("Lane" + l2pid + "_Lane" + l1pid)
		regionId = MakeId("Region_L" + l2pid + "_L" + l1pid)
	}

	info1 := hdmap.ObjectOverlapInfo{
		Id: MakeId(l1pid),
		OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
			LaneOverlapInfo: &hdmap.LaneOverlapInfo{
				StartS:          &start1,
				EndS:            &end1,
				RegionOverlapId: regionId,
			},
		},
	}
	info2 := hdmap.ObjectOverlapInfo{
		Id: MakeId(l2pid),
		OverlapInfo: &hdmap.ObjectOverlapInfo_LaneOverlapInfo{
			LaneOverlapInfo: &hdmap.LaneOverlapInfo{
				StartS:          &start2,
				EndS:            &end2,
				RegionOverlapId: regionId,
			},
		},
	}

	infos := []*hdmap.ObjectOverlapInfo{&info1, &info2}

	regionInfo := &hdmap.RegionOverlapInfo{
		Id:      regionId,
		Polygon: []*hdmap.Polygon{olPoly},
	}

	return &hdmap.Overlap{
		Id:            olId,
		Object:        infos,
		RegionOverlap: []*hdmap.RegionOverlapInfo{regionInfo},
	}
}

func IsLaneInJunction(lanepid string) bool {
	tSql := `select contains(c.geometry,l.geometry) from ibd_lane_link l,ibd_object_cross c where lane_link_pid = ?;`
	rs := db.QueryRow(tSql, lanepid)
	var virtual int
	if err := rs.Scan(&virtual); err != nil {
		if err != sql.ErrNoRows {
			log.Errorf("测试车道(pid:%s)是否在路口内错误，详情: %v", lanepid, err)
		}
	}
	return virtual != 0
}

func CaclLaneStartEnd(l1pid, l2pid string) (float64, float64, float64, float64) {
	rs := db.QueryRow(`SELECT Line_Locate_Point (g1, startpoint (ST_Intersection (g1, ig))) * st_length (g1) AS s1,
    Line_Locate_Point (g1, endpoint (ST_Intersection (g1, ig))) * st_length (g1) e1,
    Line_Locate_Point (g2, startpoint (ST_Intersection (g2, ig))) * st_length (g2) s2,
    Line_Locate_Point (g2, endpoint (ST_Intersection (g2, ig))) * st_length (g2) e2
FROM (
        SELECT p1.geom AS g1,
            p2.geom AS g2,
            ST_Intersection (p1.G, p2.G) AS ig
        FROM (
                SELECT ST_MakePolygon (
                        LineMerge (
                            COLLECT (
                                addpoint (l.bgeom, endpoint (r.bgeom)),
                                addpoint (r.bgeom, startpoint (l.bgeom), 0)
                            )
                        )
                    ) AS G,
                    l.geom
                FROM (
                        SELECT bgeom,
                            geom
                        FROM aodx_lane_boundary
                        WHERE lane_link_pid = ?
                        ORDER BY bseq
                        LIMIT 1
                    ) l, (
                        SELECT bgeom
                        FROM aodx_lane_boundary
                        WHERE lane_link_pid = ?
                        ORDER BY bseq
                        LIMIT 1, 2
                    ) r
            ) p1, (
                SELECT ST_MakePolygon (
                        LineMerge (
                            COLLECT (
                                addpoint (l.bgeom, endpoint (r.bgeom)),
                                addpoint (r.bgeom, startpoint (l.bgeom), 0)
                            )
                        )
                    ) AS G,
                    l.geom
                FROM (
                        SELECT bgeom,
                            geom
                        FROM aodx_lane_boundary
                        WHERE lane_link_pid = ?
                        ORDER BY bseq
                        LIMIT 1
                    ) l, (
                        SELECT bgeom
                        FROM aodx_lane_boundary
                        WHERE lane_link_pid = ?
                        ORDER BY bseq
                        LIMIT 1, 2
                    ) r
            ) p2
    );`, l1pid, l1pid, l2pid, l2pid)
	var start1, end1, start2, end2 sql.NullFloat64
	if err := rs.Scan(&start1, &end1, &start2, &end2); err != nil {
		if err == sql.ErrNoRows {
			start1.Float64 = 0.0
			end1.Float64 = 0.1
			start2.Float64 = 0.0
			end2.Float64 = 0.1
		} else {
			log.Errorf("计算lane(pid:%s)lane(pid:%s)polygon startS, endS失败~, 详情：%v", l1pid, l2pid, err)
		}
	}
	return start1.Float64, end1.Float64, start2.Float64, end2.Float64
}

func MakeSignals() []*hdmap.Signal {
	sss := []*hdmap.Signal{}
	rows, err := db.Query("SELECT object_pid from ibd_object_traffic_lights;")
	if err != nil {
		log.Errorf("make signals error, detail:%s", err)
		return sss
	}
	pids := []string{}
	for rows.Next() {
		var pid string
		err := rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		pids = append(pids, pid)
	}

	st := time.Now()
	total := len(pids)
	bar := pb.StartNew(total)
	for _, pid := range pids {
		// st := time.Now()
		sss = append(sss, MakeSignal(pid))
		bar.Increment()
		// log.Infof("progress(%d/%d),make signal(pid:%s), time cost: %fs ~\n", i+1, total, pid, time.Since(st).Seconds())
	}
	bar.Finish()
	fmt.Printf("make all %d signals, time cost:%fs ~\n", len(pids), time.Since(st).Seconds())
	return sss
}

func MakeSignal(pid string) *hdmap.Signal {
	sqlStr := `SELECT 
					orientation,
					row_number,
					column_number,
					ST_AsBinary(St_Transform(MakePointZ(centre_x,centre_y,centre_z,4549),32650)) as cpt,
					ST_AsBinary(transform(setsrid(geometry,4549),32650)) as geom
				FROM ibd_object_traffic_lights WHERE object_pid = ?;`
	rs := db.QueryRow(sqlStr, pid)
	var cpt wkb.Point
	var poly wkb.Polygon
	var ori, row, col int
	if err := rs.Scan(&ori, &row, &col, &cpt, &poly); err != nil {
		log.Errorf("rows scan error, details:%s", err)
		return &hdmap.Signal{}
	}

	points := []*hdmap.PointENU{}
	if poly.Layout().ZIndex() != -1 {
		stride := poly.Layout().Stride()
		flat := poly.FlatCoords()
		for i := 0; i < len(flat); i = i + stride {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}
	x, y, z := cpt.X(), cpt.Y(), cpt.Z()
	if !xyz {
		z = 0.0
	}
	sub := hdmap.Subsignal{
		Id:   MakeId("sub000"),
		Type: hdmap.Subsignal_CIRCLE.Enum(),
		Location: &hdmap.PointENU{
			X: &x,
			Y: &y,
			Z: &z,
		},
	}
	sub1 := sub
	*sub1.Location.Z += 0.3
	sub2 := sub
	*sub1.Location.Z -= 0.3
	subs := []*hdmap.Subsignal{&sub1, &sub, &sub2}

	//muti stline?如果是多停止线则分裂

	relStoplinePid := GetSignalStoplineId(pid)
	stopline := MakeStopline(relStoplinePid)
	//暂时都只有一个curve
	return &hdmap.Signal{
		Id: MakeId(pid),
		Boundary: &hdmap.Polygon{
			Point: points,
		},
		Subsignal: subs,
		OverlapId: GetSignalOverlaps(pid),
		Type:      GetSignalType(ori, row, col), //要按照行列计算类型
		StopLine:  stopline,
		SignInfo: []*hdmap.SignInfo{
			&hdmap.SignInfo{
				Type: hdmap.SignInfo_None.Enum(),
			},
		},
	}
}

func GetSignalType(ori, row, col int) *hdmap.Signal_Type {
	switch ori {
	case 1: //垂直，默认col=1
		switch row { //横排数
		case 1:
			return hdmap.Signal_SINGLE.Enum()
		case 2:
			return hdmap.Signal_MIX_2_VERTICAL.Enum()
		case 3:
			return hdmap.Signal_MIX_3_VERTICAL.Enum()
		}
	case 2: //水平，默认row=1
		switch col { //竖排数
		case 1:
			return hdmap.Signal_SINGLE.Enum()
		case 2:
			return hdmap.Signal_MIX_2_HORIZONTAL.Enum()
		case 3:
			return hdmap.Signal_MIX_3_HORIZONTAL.Enum()
		}
	}
	return hdmap.Signal_UNKNOWN.Enum()
}

func GetSignalStoplineId(signalpid string) string {
	rs := db.QueryRow(`SELECT object_pid FROM IBD_OBJECT_COMBINANT
							WHERE object_type = 7 and  combinant_pid in (
								SELECT combinant_pid 
								FROM IBD_OBJECT_COMBINANT 
								WHERE object_type = 17 and object_pid = ?) 
							group by object_pid;`, signalpid)
	var pid string
	if err := rs.Scan(&pid); err != nil {
		log.Errorf("make signal(pid:%s)→stopline error, detail:%s", signalpid, err)
	}
	return pid //返回一条即可
}

func MakeStopline(stoplinepid string) []*hdmap.Curve {
	if stoplinepid == "" {
		return []*hdmap.Curve{}
	}
	sqlStr := `SELECT ST_AsBinary(transform(setsrid(geometry, 4549), 32650)) 
	FROM ibd_object_stopline WHERE object_pid = ?;`
	rs := db.QueryRow(sqlStr, stoplinepid)
	var line wkb.LineString
	if err := rs.Scan(&line); err != nil {
		log.Errorf("stopline(pid:%s) row scan error, details:%s", stoplinepid, err)
		return []*hdmap.Curve{}
	}
	points := []*hdmap.PointENU{}
	if line.Layout().ZIndex() != -1 {
		flat := line.FlatCoords()
		for i := 0; i < len(flat); i = i + line.Stride() {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}
	lineSegm := &hdmap.CurveSegment{
		CurveType: &hdmap.CurveSegment_LineSegment{
			LineSegment: &hdmap.LineSegment{
				Point: points,
			},
		},
	}
	return []*hdmap.Curve{
		&hdmap.Curve{
			Segment: []*hdmap.CurveSegment{lineSegm},
		},
	}
}

//GetSignalOverlaps Signal目前只计算与lane&junction的ovlerlap关系
func GetSignalOverlaps(signalpid string) []*hdmap.Id {
	ids := []*hdmap.Id{}
	//get lane&signal overlaps
	laneOverIds := GetObjLaneIds(signalpid)
	ids = append(ids, laneOverIds...)
	//暂时不需要处理junction与signal的关系，直接返回
	return ids
}

func MakeCrosswalks() []*hdmap.Crosswalk {
	cws := []*hdmap.Crosswalk{}
	rows, err := db.Query("SELECT object_pid from ibd_object_cross_walk;")
	if err != nil {
		log.Errorf("make crosswalk error, detail:%s", err)
	}
	pids := []string{}
	for rows.Next() {
		var pid string
		err := rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		pids = append(pids, pid)
	}
	st := time.Now()
	total := len(pids)
	bar := pb.StartNew(total)
	for _, pid := range pids {
		// st := time.Now()
		cws = append(cws, MakeCrosswalk(pid))
		bar.Increment()
		// fmt.Printf("progress(%d/%d),make crosswalk(pid:%s), time cost: %fs ~\n", i+1, total, pid, time.Since(st).Seconds())
	}
	bar.Finish()
	fmt.Printf("make all %d crosswalks, time cost:%fs ~\n", len(pids), time.Since(st).Seconds())
	return cws
}

func MakeCrosswalk(pid string) *hdmap.Crosswalk {
	sqlStr := `SELECT ST_AsBinary(transform(setsrid(geometry, 4549), 32650)) 
	FROM ibd_object_cross_walk WHERE object_pid = ?;`
	rs := db.QueryRow(sqlStr, pid)
	var poly wkb.Polygon
	if err := rs.Scan(&poly); err != nil {
		log.Errorf("rows scan error, details:%s", err)
		return &hdmap.Crosswalk{}
	}

	points := []*hdmap.PointENU{}
	if poly.Layout().ZIndex() != -1 {
		flat := poly.FlatCoords()
		for i := 0; i < len(flat); i = i + 3 {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}

	return &hdmap.Crosswalk{
		Id: MakeId(pid),
		Polygon: &hdmap.Polygon{
			Point: points,
		},
		OverlapId: GetCrosswalkOverlaps(pid),
	}
}

//GetCrosswalkOverlaps crosswalk目前计算与lane&junction的ovlerlap关系
func GetCrosswalkOverlaps(crosswalkpid string) []*hdmap.Id {
	ids := []*hdmap.Id{}
	//get lane&crosswalk overlaps
	laneOverIds := GetObjLaneIds(crosswalkpid)
	ids = append(ids, laneOverIds...)
	//get junction&crosswalk overlaps
	junSql := `select c.object_pid 
				from ibd_object_cross c, ibd_object_cross_walk w
				where contains(c.geometry,w.geometry) = 1 and w.object_pid = ?;`
	rows, err := db.Query(junSql, crosswalkpid)
	if err != nil {
		log.Errorf("get junction&crosswalk overlaps error, detail:%s", err)
		return ids
	}
	for rows.Next() {
		var jpid string
		err := rows.Scan(&jpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId("Junction"+jpid+"_CrossWalk"+crosswalkpid))
	}

	return ids
}

func GetObjLaneIds(objectpid string) []*hdmap.Id {
	ids := []*hdmap.Id{}
	rows, err := db.Query("select lane_link_pid,object_type from ibd_object_lane_link_rel where object_pid = ?;", objectpid)
	if err != nil {
		log.Errorf("get object&lane overlaps error, detail:%s", err)
		return ids
	}
	for rows.Next() {
		var lane string
		var otype int
		err := rows.Scan(&lane, &otype)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ostr := "Object"
		switch otype {
		case 7:
			ostr = "StopLine"
		case 17:
			ostr = "Signal"
		case 18:
			ostr = "CrossWalk"
		}
		ids = append(ids, MakeId("Lane"+lane+"_"+ostr+objectpid))
	}
	return ids
}

func MakeJunctions() []*hdmap.Junction {
	junctions := []*hdmap.Junction{}
	rows, err := db.Query("SELECT object_pid from ibd_object_cross;")
	if err != nil {
		log.Errorf("make junctions error, detail:%s", err)
	}
	pids := []string{}
	for rows.Next() {
		var pid string
		err := rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		pids = append(pids, pid)
	}
	st := time.Now()
	total := len(pids)
	bar := pb.StartNew(total)

	for _, pid := range pids {
		// st := time.Now()
		junctions = append(junctions, MakeJunction(pid))
		bar.Increment()
		// fmt.Printf("progress(%d/%d),make junction(pid:%s), time cost: %fs ~\n", i+1, total, pid, time.Since(st).Seconds())
	}
	bar.Finish()
	fmt.Printf("make all %d junctions, time cost: %fs ~\n", len(pids), time.Since(st).Seconds())
	return junctions
}

func MakeJunction(pid string) *hdmap.Junction {
	sqlStr := `SELECT ST_AsBinary(transform(setsrid(geometry, 4549), 32650)) 
	FROM ibd_object_cross WHERE object_pid = ?;`
	rs := db.QueryRow(sqlStr, pid)
	var poly wkb.Polygon
	if err := rs.Scan(&poly); err != nil {
		log.Errorf("rows scan error, details:%s", err)
		return &hdmap.Junction{}
	}

	points := []*hdmap.PointENU{}
	if poly.Layout().ZIndex() != -1 {
		flat := poly.FlatCoords()
		for i := 0; i < len(flat); i = i + poly.Stride() {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}

	return &hdmap.Junction{
		Id: MakeId(pid),
		Polygon: &hdmap.Polygon{
			Point: points,
		},
		OverlapId: GetJunctionOverlaps(pid),
		// Type:      hdmap.Junction_CROSS_ROAD.Enum(),
	}
}

func GetJunctionOverlaps(junctionpid string) []*hdmap.Id {
	ids := []*hdmap.Id{}
	//junction&lane
	laneSql := `select j.object_pid,l.lane_link_pid from ibd_object_cross j, ibd_lane_link l
				where contains(j.geometry,l.geometry) = 1 and j.object_pid = ?;`
	rows, err := db.Query(laneSql, junctionpid)
	if err != nil {
		log.Errorf("get junction's overlap lanes error, detail:%s", err)
		goto crosswalk
	}
	for rows.Next() {
		var jpid, lpid string
		err := rows.Scan(&jpid, &lpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId("Junction"+jpid+"_Lane"+lpid))
	}

crosswalk:
	walkSql := `select w.object_pid from ibd_object_cross c, ibd_object_cross_walk w
	where contains(c.geometry,w.geometry) = 1 and c.object_pid = ?;`
	rows, err = db.Query(walkSql, junctionpid)
	if err != nil {
		log.Errorf("get junction&crosswalk overlaps error, detail:%s", err)
		return ids
	}
	for rows.Next() {
		var cwpid string
		err := rows.Scan(&cwpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId("Junction"+junctionpid+"_CrossWalk"+cwpid))
	}
	//暂时不需要处理junction&signal的overlap直接返回
	return ids
}

func MakeLanes() []*hdmap.Lane {
	st := time.Now()
	lanes := []*hdmap.Lane{}
	rows, err := db.Query("SELECT lane_link_pid from IBD_LANE_LINK;")
	if err != nil {
		log.Errorf("查询车道参考线失败, details: %s", err)
		return lanes
	}
	laneLinks := []string{}
	for rows.Next() {
		var pid string
		err := rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		laneLinks = append(laneLinks, pid)
	}

	total := len(laneLinks)
	bar := pb.StartNew(total)

	for _, laneLink := range laneLinks {
		// st := time.Now()
		lanes = append(lanes, MakeLane(laneLink))
		bar.Increment()
		// fmt.Printf("progress(%d/%d),make lane(pid:%s), time cost: %fs ~\n", i+1, total, laneLink, time.Since(st).Seconds())
	}
	bar.Finish()
	fmt.Printf("make all %d lane, time cost: %fs ~\n", len(lanes), time.Since(st).Seconds())

	return lanes
}

func MakeLanesFast(lanepids []string, bar *pb.ProgressBar) map[string]*hdmap.Lane {
	st := time.Now()
	lanes := make(map[string]*hdmap.Lane)
	// laneList := make(map[string]*LaneHelper)
	//计算车道
	rows, err := db.Query(`SELECT 
					lane_link_pid,
					link_pid,
					s_lane_node_pid as start,
					e_lane_node_pid as end, 
					seq_num as seq, 
					lane_dir,
					lane_driving_dir,
					lane_type,
					max_speed,
					width,
					length,
					ST_AsBinary(ST_Transform(SetSRID(GEOMETRY,4549),32650)) as line
				FROM IBD_LANE_LINK;`)
	if err != nil {
		log.Errorf("获取车道列表失败，详情：%s", err)
		return nil
	}
	defer rows.Close()
	for rows.Next() {
		var line wkb.LineString
		var lanepid string
		lhp := LaneHelper{}
		if err := rows.Scan(&lhp.Pid, &lhp.Link, &lhp.Start, &lhp.End, &lhp.Seq, &lhp.Direction, &lhp.Turn, &lhp.Type, &lhp.Speed, &lhp.Width, &lhp.Length, &line); err != nil {
			log.Errorf("read all lanes failed, details: %v", err)
			continue
		}
		lhp.Length = lhp.Length / 1000.0
		startpos := hdmap.PointENU{}
		points := []*hdmap.PointENU{}
		if line.Layout().ZIndex() != -1 {
			flat := line.FlatCoords()
			for i := 0; i < len(flat); i = i + 3 {
				x, y, z := flat[i], flat[i+1], flat[i+2]
				if !xyz {
					z = 0.0
				}
				pt := hdmap.PointENU{
					X: &x,
					Y: &y,
					Z: &z,
				}
				if i == 0 { //first point
					startpos = pt
				}
				points = append(points, &pt)
			}
		}
		s := 0.0
		centralSegm := &hdmap.CurveSegment{
			CurveType: &hdmap.CurveSegment_LineSegment{
				LineSegment: &hdmap.LineSegment{
					Point: points,
				},
			},
			S:             &s,
			Length:        &lhp.Length,
			StartPosition: &startpos,
		}
		central := hdmap.Curve{
			Segment: []*hdmap.CurveSegment{centralSegm},
		}

		leftBpid, rightBpid := GetLaneBoundaryIds(&lhp)
		//左边界left
		leftCurve := MakeLaneBoundaryCurveSegment(lanepid, leftBpid)

		lbts := hdmap.LaneBoundaryType{
			S:     leftCurve.S,
			Types: GetLaneBoundaryTypes(leftBpid),
		}
		left := &hdmap.LaneBoundary{
			Curve: &hdmap.Curve{
				Segment: []*hdmap.CurveSegment{leftCurve}, //通常segment也只有一段
			},
			Length:       leftCurve.Length,
			BoundaryType: []*hdmap.LaneBoundaryType{&lbts},
			Virtual:      IsBoundaryVirtual(leftBpid),
		}
		//右边界right
		rightCurve := MakeLaneBoundaryCurveSegment(lanepid, rightBpid)
		rlbts := hdmap.LaneBoundaryType{
			S:     rightCurve.S,
			Types: GetLaneBoundaryTypes(rightBpid),
		}
		right := &hdmap.LaneBoundary{
			Curve: &hdmap.Curve{
				Segment: []*hdmap.CurveSegment{rightCurve}, //通常segment也只有一段
			},
			Length:       rightCurve.Length,
			BoundaryType: []*hdmap.LaneBoundaryType{&rlbts},
			Virtual:      IsBoundaryVirtual(leftBpid),
		}

		lhp.Speed = lhp.Speed / 3.6 // km/h → m/s
		direction := hdmap.Lane_FORWARD.Enum()
		if lhp.Direction == 3 {
			direction = hdmap.Lane_BACKWARD.Enum()
		}

		//Neighbors
		leftNeighborForwardIds := []*hdmap.Id{}
		leftNeighborReverseIds := []*hdmap.Id{}
		tmpPid, tmpDir := GetLaneLeftNeighbor(&lhp)
		if tmpPid != "" {
			if tmpDir == 3 {
				leftNeighborReverseIds = append(leftNeighborReverseIds, MakeId(tmpPid))
			} else {
				leftNeighborForwardIds = append(leftNeighborForwardIds, MakeId(tmpPid))
			}
		}
		rightNeighborForwardIds := []*hdmap.Id{}
		rightNeighborReverseIds := []*hdmap.Id{}
		tmpPid, tmpDir = GetLaneRightNeighbor(&lhp)
		if tmpPid != "" {
			if tmpDir == 3 {
				rightNeighborReverseIds = append(rightNeighborReverseIds, MakeId(tmpPid))
			} else {
				rightNeighborForwardIds = append(rightNeighborForwardIds, MakeId(tmpPid))
			}
		}
		leftSamples := CalcLaneBoundarySample(lanepid, leftBpid)
		rightSamples := CalcLaneBoundarySample(lanepid, rightBpid)
		leftRoadBpid, rightRoadBpid := GetRoadBoundaryIds(lhp.Link)
		leftRoadSamples := CalcLaneRoadBoundarySample(lhp.Link, lhp.Pid, leftRoadBpid)
		rightRoadSamples := CalcLaneRoadBoundarySample(lhp.Link, lhp.Pid, rightRoadBpid)

		var isWaiting bool
		//默认为非待转车道，值为7/134217728则为代转车道
		if lhp.Type == 134217728 {
			isWaiting = true
		}

		lane, ok := lanes[lanepid]
		if ok {
			lane.CentralCurve = &central
		} else {
			lanes[lanepid] = &hdmap.Lane{
				Id:                         MakeId(lanepid),
				CentralCurve:               &central,
				LeftBoundary:               left,
				RightBoundary:              right,
				Length:                     &lhp.Length,
				SpeedLimit:                 &lhp.Speed,
				OverlapId:                  GetLaneOverlapIds(&lhp),
				SuccessorId:                GetLaneSuccessorIds(&lhp),
				PredecessorId:              GetPredecessorIds(&lhp),
				LeftNeighborForwardLaneId:  leftNeighborForwardIds,
				RightNeighborForwardLaneId: rightNeighborForwardIds,
				LeftNeighborReverseLaneId:  leftNeighborReverseIds,
				RightNeighborReverseLaneId: rightNeighborReverseIds,
				Type:                       GetLaneType(lhp.Type),
				Turn:                       GetLaneTurn(lhp.Turn),
				JunctionId:                 GetLaneJunction(&lhp),
				LeftSample:                 leftSamples,
				RightSample:                rightSamples,
				LeftRoadSample:             leftRoadSamples,
				RightRoadSample:            rightRoadSamples,
				Direction:                  direction,
				// SelfReverseLaneId:          ids,
				IsWaitingLane: &isWaiting,
			}
		}
	}
	fmt.Printf("make all %d lane, time cost: %fs ~\n", len(lanes), time.Since(st).Seconds())
	return lanes
}

func MakeLane(lanepid string) *hdmap.Lane {
	//获取车道中心线信息
	r := db.QueryRow(`SELECT 
						lane_link_pid,
						link_pid,
						s_lane_node_pid as start,
						e_lane_node_pid as end, 
						seq_num as seq, 
						lane_dir,
						lane_driving_dir,
						lane_type,
						max_speed,
						width,
						length,
						ST_AsBinary(ST_Transform(SetSRID(GEOMETRY,4549),32650)) as line
					  FROM IBD_LANE_LINK WHERE LANE_LINK_PID = ?;`, lanepid)

	var line wkb.LineString
	lhp := LaneHelper{}
	if err := r.Scan(&lhp.Pid, &lhp.Link, &lhp.Start, &lhp.End, &lhp.Seq, &lhp.Direction, &lhp.Turn, &lhp.Type, &lhp.Speed, &lhp.Width, &lhp.Length, &line); err != nil {
		log.Errorf("读取车道(pid:%s)数据失败, 详情：%s", lanepid, err)
		return nil
	}
	lhp.Length = lhp.Length / 1000.0
	startpos := hdmap.PointENU{}
	points := []*hdmap.PointENU{}
	if line.Layout().ZIndex() != -1 {
		flat := line.FlatCoords()
		for i := 0; i < len(flat); i = i + 3 {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			pt := hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			}
			if i == 0 { //first point
				startpos = pt
			}
			points = append(points, &pt)
		}
	}
	s := 0.0
	centralSegm := &hdmap.CurveSegment{
		CurveType: &hdmap.CurveSegment_LineSegment{
			LineSegment: &hdmap.LineSegment{
				Point: points,
			},
		},
		S:             &s,
		Length:        &lhp.Length,
		StartPosition: &startpos,
	}
	central := hdmap.Curve{
		Segment: []*hdmap.CurveSegment{centralSegm},
	}
	leftBpid, rightBpid := GetLaneBoundaryIds(&lhp)

	//左边界left
	leftCurve := MakeLaneBoundaryCurveSegment(lanepid, leftBpid)

	lbts := hdmap.LaneBoundaryType{
		S:     leftCurve.S,
		Types: GetLaneBoundaryTypes(leftBpid),
	}
	left := &hdmap.LaneBoundary{
		Curve: &hdmap.Curve{
			Segment: []*hdmap.CurveSegment{leftCurve}, //通常segment也只有一段
		},
		Length:       leftCurve.Length,
		BoundaryType: []*hdmap.LaneBoundaryType{&lbts},
		Virtual:      IsBoundaryVirtual(leftBpid),
	}
	//右边界right
	rightCurve := MakeLaneBoundaryCurveSegment(lanepid, rightBpid)
	rlbts := hdmap.LaneBoundaryType{
		S:     rightCurve.S,
		Types: GetLaneBoundaryTypes(rightBpid),
	}
	right := &hdmap.LaneBoundary{
		Curve: &hdmap.Curve{
			Segment: []*hdmap.CurveSegment{rightCurve}, //通常segment也只有一段
		},
		Length:       rightCurve.Length,
		BoundaryType: []*hdmap.LaneBoundaryType{&rlbts},
		Virtual:      IsBoundaryVirtual(leftBpid),
	}

	lhp.Speed = lhp.Speed / 3.6 // km/h → m/s
	direction := hdmap.Lane_FORWARD.Enum()
	if lhp.Direction == 3 {
		direction = hdmap.Lane_BACKWARD.Enum()
	}

	//Neighbors
	leftNeighborForwardIds := []*hdmap.Id{}
	leftNeighborReverseIds := []*hdmap.Id{}
	tmpPid, tmpDir := GetLaneLeftNeighbor(&lhp)
	if tmpPid != "" {
		if tmpDir == 3 {
			leftNeighborReverseIds = append(leftNeighborReverseIds, MakeId(tmpPid))
		} else {
			leftNeighborForwardIds = append(leftNeighborForwardIds, MakeId(tmpPid))
		}
	}
	rightNeighborForwardIds := []*hdmap.Id{}
	rightNeighborReverseIds := []*hdmap.Id{}
	tmpPid, tmpDir = GetLaneRightNeighbor(&lhp)
	if tmpPid != "" {
		if tmpDir == 3 {
			rightNeighborReverseIds = append(rightNeighborReverseIds, MakeId(tmpPid))
		} else {
			rightNeighborForwardIds = append(rightNeighborForwardIds, MakeId(tmpPid))
		}
	}
	leftSamples := CalcLaneBoundarySample(lanepid, leftBpid)
	rightSamples := CalcLaneBoundarySample(lanepid, rightBpid)
	leftRoadBpid, rightRoadBpid := GetRoadBoundaryIds(lhp.Link)
	leftRoadSamples := CalcLaneRoadBoundarySample(lhp.Link, lhp.Pid, leftRoadBpid)
	rightRoadSamples := CalcLaneRoadBoundarySample(lhp.Link, lhp.Pid, rightRoadBpid)

	var isWaiting bool
	//默认为非待转车道，值为7/134217728则为代转车道
	if lhp.Type == 134217728 {
		isWaiting = true
	}

	return &hdmap.Lane{
		Id:                         MakeId(lhp.Pid),
		CentralCurve:               &central,
		LeftBoundary:               left,
		RightBoundary:              right,
		Length:                     &lhp.Length,
		SpeedLimit:                 &lhp.Speed,
		OverlapId:                  GetLaneOverlapIds(&lhp),
		SuccessorId:                GetLaneSuccessorIds(&lhp),
		PredecessorId:              GetPredecessorIds(&lhp),
		LeftNeighborForwardLaneId:  leftNeighborForwardIds,
		RightNeighborForwardLaneId: rightNeighborForwardIds,
		LeftNeighborReverseLaneId:  leftNeighborReverseIds,
		RightNeighborReverseLaneId: rightNeighborReverseIds,
		Type:                       GetLaneType(lhp.Type),
		Turn:                       GetLaneTurn(lhp.Turn),
		JunctionId:                 GetLaneJunction(&lhp),
		LeftSample:                 leftSamples,
		RightSample:                rightSamples,
		LeftRoadSample:             leftRoadSamples,
		RightRoadSample:            rightRoadSamples,
		Direction:                  direction,
		// SelfReverseLaneId:          ids,
		IsWaitingLane: &isWaiting,
	}
}

func GetLaneOverlapIds(lhp *LaneHelper) []*hdmap.Id {
	ids := []*hdmap.Id{}
	//get lane→overlaps
	rows, err := db.Query("select object_pid, object_type from ibd_object_lane_link_rel where lane_link_pid = ? order by object_type;", lhp.Pid)
	if err != nil {
		log.Errorf("get lane(pid:%s) overlaps error, detail:%s", lhp.Pid, err)
	}
	defer rows.Close()
	opids := []string{}
	otypes := []int{}
	for rows.Next() {
		var opid string
		var otype int
		err := rows.Scan(&opid, &otype)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		opids = append(opids, opid)
		otypes = append(otypes, otype)
	}
	//lane&object overlaps infos
	for i := 0; i < len(opids); i++ {
		ostr := "Object"
		//暂时只识别信号灯ObjectOverlapInfo_SignalOverlapInfo和人行横道ObjectOverlapInfo_CrosswalkOverlapInfo,
		switch otypes[i] {
		case 17:
			ostr = "Signal"
		case 18:
			ostr = "CrossWalk"
		default:
			continue
		}

		//如果灯上有两条停止线，则只关联与该lane相关的停止线，+停止线id
		id := "Lane" + lhp.Pid + "_" + ostr + opids[i]
		if otypes[i] == 17 {
			pid := HasMutiStopLine(lhp.Pid, opids[i])
			if pid != "" {
				id = id + "_Stopline" + pid
			}
		}
		ids = append(ids, MakeId(id))
	}
	// junction&lane overlaps,ObjectOverlapInfo_JunctionOverlapInfo
	//如果lane跟junction有重叠则说明是在路口内的virtuallane,则下一步lane&lane的重叠无需计算
	rows, err = db.Query(`select c.object_pid from ibd_object_cross c, ibd_lane_link l
		where contains(c.geometry,l.geometry) = 1 and l.lane_link_pid = ?;`, lhp.Pid)
	if err != nil {
		log.Errorf("get lane(pid:%s)→junction overlaps error, details:%s", lhp.Pid, err)
		goto next
	}
	for rows.Next() {
		var jpid string
		err := rows.Scan(&jpid)
		if err != nil {
			log.Errorf("rows scan lane's(pid:%s) overlap junction error, details:%s", lhp.Pid, err)
			continue
		}
		id := MakeId("Junction" + jpid + "_Lane" + lhp.Pid)
		ids = append(ids, id)
	}

next:
	//lane&lane overlaps, id的命名组织是seq序号小的在前，即左侧在前，右侧在后
	rows, err = db.Query(`SELECT lane_link_pid
	FROM ibd_lane_link
	WHERE s_lane_node_pid = (
			SELECT s_lane_node_pid
			FROM ibd_lane_link
			WHERE lane_link_pid = ?
		)
		AND lane_link_pid != ?;`, lhp.Pid, lhp.Pid)
	if err != nil {
		log.Error("get start node lanelane overlap error~, details:%v", err)
		goto endnode
	}
	for rows.Next() {
		var lpid string
		err := rows.Scan(&lpid)
		if err != nil {
			continue
		}
		olId := MakeId("Lane" + lhp.Pid + "_Lane" + lpid)
		if lhp.Pid > lpid {
			olId = MakeId("Lane" + lpid + "_Lane" + lhp.Pid)
		}
		ids = append(ids, olId)
	}

endnode:
	rows, err = db.Query(`SELECT lane_link_pid
	FROM ibd_lane_link
	WHERE e_lane_node_pid = (
			SELECT e_lane_node_pid
			FROM ibd_lane_link
			WHERE lane_link_pid = ?
		)
		AND lane_link_pid != ?;`, lhp.Pid, lhp.Pid)

	if err != nil {
		log.Errorf("get end node lanelane overlap error~, details:%v", err)
		return ids
	}
	for rows.Next() {
		var lpid string
		err := rows.Scan(&lpid)
		if err != nil {
			continue
		}
		olId := MakeId("Lane" + lhp.Pid + "_Lane" + lpid)
		if lhp.Pid > lpid {
			olId = MakeId("Lane" + lpid + "_Lane" + lhp.Pid)
		}
		ids = append(ids, olId)
	}
	return ids
}

//IsLaneLaneOverlap 判断lane是否有重叠，排除路口内虚拟车道, 0→error，1→plus，2→reduce
func HasLaneLaneOverlap(lhp *LaneHelper) int {
	//如果在路口内部，则不处理
	rs := db.QueryRow(`select contains(c.geometry,l.geometry) 
	from ibd_object_cross c, ibd_lane_link l where l.lane_link_pid = ?`, lhp.Pid)
	var virtual bool
	if err := rs.Scan(&virtual); err != nil {
		log.Errorf("query lane(pid:%s)'s virtual attributes error, details:%v", lhp.Pid, err)
	}
	if virtual {
		return 0
	}
	//非virtual则查询首尾节点是否重叠
	//lane&lane overlaps
	//plus
	rows, err := db.Query(`select lane_link_pid from ibd_lane_link where s_lane_node_pid = 
						  (select s_lane_node_pid from ibd_lane_link where lane_link_pid = ?)
						  order by lane_link_pid;`, lhp.Pid)
	if err != nil {
		log.Errorf("get lane&lane(pid:%s) overlaps error, details:%s", lhp.Pid, err)
	}
	var lpids []string
	for rows.Next() {
		var lpid string
		err := rows.Scan(&lpid)
		if err != nil {
			log.Errorf("rows scan lane&lane(pid:%s) error, details:%s", lhp.Pid, err)
			continue
		}
		lpids = append(lpids, lpid)
	}
	if len(lpids) == 2 {
		return 1 //puls
	}
	if len(lpids) != 0 {
		log.Errorf("calc lane&lane overlap error, details: lane&lane num = %d", len(lpids))
	}
	//reduce
	rows, err = db.Query(`select lane_link_pid from ibd_lane_link where e_lane_node_pid = 
						  (select e_lane_node_pid from ibd_lane_link where lane_link_pid = ?)
						  order by lane_link_pid;`, lhp.Pid)
	if err != nil {
		log.Errorf("get lane&lane(pid:%s) overlaps error, details:%s", lhp.Pid, err)
	}
	lpids = []string{}
	for rows.Next() {
		var lpid string
		err := rows.Scan(&lpid)
		if err != nil {
			log.Errorf("rows scan lane&lane(pid:%s) error, details:%s", lhp.Pid, err)
			continue
		}
		lpids = append(lpids, lpid)
	}
	if len(lpids) == 2 {
		return 2 //puls
	}
	if len(lpids) != 0 {
		log.Errorf("calc lane&lane overlap error, details: lane&lane num = %d", len(lpids))
	}
	return 0
}

func GetLaneBoundaryTypes(boundarypid string) []hdmap.LaneBoundaryType_Type {
	btypes := []hdmap.LaneBoundaryType_Type{}
	//先获取是否是虚拟
	var btp, bcnt, trav int
	strSql := `SELECT boundary_type,marking_count,traversal FROM ibd_lane_boundary WHERE lane_boundary_pid = ?;`
	rs := db.QueryRow(strSql, boundarypid)
	if err := rs.Scan(&btp, &bcnt, &trav); err != nil {
		log.Errorf("get lane boundary types error, details:%v", err)
		return []hdmap.LaneBoundaryType_Type{hdmap.LaneBoundaryType_DOTTED_WHITE}
	}
	lbtp := hdmap.LaneBoundaryType_DOTTED_WHITE
	switch btp {
	case 2:
		switch trav {
		case 0:
			lbtp = hdmap.LaneBoundaryType_SOLID_YELLOW //默认标线
		}
		lbtp = hdmap.LaneBoundaryType_DOTTED_WHITE //默认标线
	case 3:
		lbtp = hdmap.LaneBoundaryType_CURB
	}

	strSql = `SELECT mark_seq_num as seq,mark_type,mark_color from ibd_marklink WHERE lane_boundary_pid = ? order by seq;`
	rows, err := db.Query(strSql, boundarypid)
	if err != nil {
		log.Errorf("get lane boundary types error, details:%v", err)
		return []hdmap.LaneBoundaryType_Type{lbtp}
	}

	for rows.Next() {
		var seq, tp, color int
		err := rows.Scan(&seq, &tp, &color)
		if err != nil {
			log.Errorf("get mark error, details:%s", err)
			continue
		}
		switch tp {
		case 1:
			switch color {
			case 1:
				lbtp = hdmap.LaneBoundaryType_SOLID_WHITE
			case 2:
				lbtp = hdmap.LaneBoundaryType_SOLID_YELLOW
			}
		case 2:
			switch color {
			case 1:
				lbtp = hdmap.LaneBoundaryType_DOTTED_WHITE
			case 2:
				lbtp = hdmap.LaneBoundaryType_DOTTED_YELLOW
			}
		}
		btypes = append(btypes, lbtp)
	}
	if len(btypes) == 0 {
		return []hdmap.LaneBoundaryType_Type{hdmap.LaneBoundaryType_DOTTED_WHITE}
	}

	return btypes
}

func GetLaneTurn(turn int) *hdmap.Lane_LaneTurn {
	switch turn {
	case 2:
		return hdmap.Lane_RIGHT_TURN.Enum()
	case 4:
		return hdmap.Lane_LEFT_TURN.Enum()
	case 8:
		return hdmap.Lane_U_TURN.Enum()
	}
	return hdmap.Lane_NO_TURN.Enum()
}

func CalcLaneBoundarySample(lanepid, boundarypid string) []*hdmap.LaneSampleAssociation {
	samples := []*hdmap.LaneSampleAssociation{}
	if boundarypid == "" {
		return samples
	}
	sqlStr := `SELECT 
					ST_M(ST_PointN(A.geom, b.x)) as sOffset,
					ST_Distance(pointN (A.geom, b.x), A.bgeom) AS width,
					b.x AS pointnum
				FROM aodx_lane_boundary AS A,
				(
					WITH RECURSIVE cnt (x) AS (
						VALUES (1)
						UNION ALL
						SELECT x + 1
						FROM cnt
						WHERE x < (
								SELECT ST_NPoints (geom)
								FROM aodx_lane_boundary
								WHERE lane_link_pid = ? and lane_boundary_pid = ?
							)
					)
					SELECT x
					FROM cnt
				) AS b
				WHERE A.lane_link_pid = ? and A.lane_boundary_pid = ? ;`

	rows, err := db.Query(sqlStr, lanepid, boundarypid, lanepid, boundarypid)
	if err != nil {
		log.Errorf("get lane boundary(%s) sample error, details:%s", boundarypid, err)
		return samples
	}
	defer rows.Close()

	for rows.Next() {
		var offset, width sql.NullFloat64
		var pointnum int
		err = rows.Scan(&offset, &width, &pointnum)
		if err != nil {
			log.Errorf("get lane boundary(%s) sample error, details:%v", boundarypid, err)
			continue
		}
		// if width.Float64 < 1.05 { //1.05
		// log.Warnf("lane(pid:%s) boundary(pid:%s) sample width < 1.05 ~", lanepid, boundarypid)
		// continue
		// }
		sample := &hdmap.LaneSampleAssociation{
			S:     &offset.Float64,
			Width: &width.Float64,
		}
		samples = append(samples, sample)
	}

	return samples
}

func CalcLaneRoadBoundarySample(linkpid, lanepid, boundarypid string) []*hdmap.LaneSampleAssociation {
	samples := []*hdmap.LaneSampleAssociation{}
	if boundarypid == "" {
		log.Warningf("link(pid:%s) boundary pid is null", linkpid)
		return samples
	}
	sqlStr := `SELECT ST_M(ST_PointN(A.geom, b.x)) as sOffset,
    ST_Distance(pointN (A.geom, b.x), A.bgeom) AS width,
    b.x AS pointnum
FROM (
        select t1.link_pid,
            t1.lane_boundary_pid,
            t1.bgeom,
            t2.geom
        from aodx_lane_boundary t1
            left outer join aodx_lane_link t2 on t1.link_pid = t2.link_pid
            and t2.lane_link_pid = ?
    ) AS A,
    (
        WITH RECURSIVE cnt (x) AS (
            VALUES (1)
            UNION ALL
            SELECT x + 1
            FROM cnt
            WHERE x < (
                    SELECT ST_NPoints (geom)
                    FROM aodx_lane_link
                    WHERE lane_link_pid = ?
                )
        )
        SELECT x
        FROM cnt
    ) AS b
WHERE A.link_pid = ?
    and A.lane_boundary_pid = ?;`

	rows, err := db.Query(sqlStr, lanepid, lanepid, linkpid, boundarypid)
	if err != nil {
		log.Errorf("get lane road boundary(pid:%s) sample error, details:%s", boundarypid, err)
		return samples
	}
	defer rows.Close()

	for rows.Next() {
		var offset, width sql.NullFloat64
		var pointnum int
		err = rows.Scan(&offset, &width, &pointnum)
		if err != nil {
			log.Errorf("get lane road boundary(%s) sample error, details:%v", boundarypid, err)
			continue
		}
		// if width.Float64 < 1.05 { //1.05
		// log.Warnf("lane(pid:%s) boundary(pid:%s) sample width < 1.05 ~", lanepid, boundarypid)
		// continue
		// }
		sample := &hdmap.LaneSampleAssociation{
			S:     &offset.Float64,
			Width: &width.Float64,
		}
		samples = append(samples, sample)
	}

	return samples
}

//GetLaneLeftNeighbor 返回lane_pid&dir pid为空则没有左车道，dir→1未知，2同向，3逆向
func GetLaneLeftNeighbor(lhp *LaneHelper) (string, int) {
	//找到左侧相邻车道
	rows, err := db.Query("SELECT lane_link_pid, seq_num, lane_dir FROM ibd_lane_link WHERE link_pid = ? ;", lhp.Link)
	if err != nil {
		log.Errorf("查找左侧相邻车道失败, 详情:%v", err)
	}
	defer rows.Close()
	lanes := make(map[int]string)
	dirs := make(map[int]int)
	for rows.Next() {
		var pid string
		var seq int
		var dir int
		err = rows.Scan(&pid, &seq, &dir)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		lanes[seq] = pid
		dirs[seq] = dir
	}

	//使用车道编号，找左右相邻车道
	left, ok := lanes[lhp.Seq-1]
	if !ok {
		//查找该道路是否有共线左车道
		// log.Warnf("车道(pid:%s,seq:%d)的左相邻车道找不到~", lhp.Pid, lhp.Seq)
		return "", 0 //"",0为没有左车道
	}
	dir := dirs[lhp.Seq-1]
	//1是未知，同向返回2，逆向返回3
	if dir == 1 {
		return left, 1
	} else if dir == lhp.Direction {
		return left, 2
	} else {
		return left, 3
	}
}

//GetLaneRightNeighbor，获取右侧相邻车道
func GetLaneRightNeighbor(lhp *LaneHelper) (string, int) {
	//找到右侧相邻车道
	rows, err := db.Query("SELECT lane_link_pid, seq_num, lane_dir FROM ibd_lane_link WHERE link_pid = ? ;", lhp.Link)
	if err != nil {
		log.Errorf("获取车道(pid:%s)的右侧相邻车道失败, 详情：%s", lhp.Pid, err)
	}
	defer rows.Close()
	lanes := make(map[int]string)
	dirs := make(map[int]int)
	for rows.Next() {
		var pid string
		var seq int
		var dir int
		err = rows.Scan(&pid, &seq, &dir)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		lanes[seq] = pid
		dirs[seq] = dir
	}

	//使用车道编号，找左右相邻车道
	right, ok := lanes[lhp.Seq+1]
	if !ok {
		//需查找道路是否有共线右车道
		// log.Warnf("车道(pid:%s,seq:%d)的右相邻车道找不到~", lhp.Pid, lhp.Seq)
		return "", 0 //"",0为没有左车道
	}
	dir := dirs[lhp.Seq+1]
	//1是未知，同向返回2，逆向返回3
	if dir == 1 {
		return right, 1
	} else if dir == lhp.Direction {
		return right, 2
	} else {
		return right, 3
	}
}

func GetLaneSuccessorIds(lhp *LaneHelper) []*hdmap.Id {
	//获取车道end节点，获取所有以该节点为起点的车道
	ids := []*hdmap.Id{}
	rows, err := db.Query("SELECT lane_link_pid FROM ibd_lane_link WHERE s_lane_node_pid = ? ;", lhp.End)
	if err != nil {
		log.Errorf("获取后继车道失败, 详情：%v", err)
	}
	defer rows.Close()
	for rows.Next() {
		var pid string
		err = rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId(pid))
	}
	return ids
}

func GetPredecessorIds(lhp *LaneHelper) []*hdmap.Id {
	//获取车道end节点，获取所有以该节点为起点的车道
	ids := []*hdmap.Id{}
	rows, err := db.Query("SELECT lane_link_pid FROM ibd_lane_link WHERE e_lane_node_pid = ? ;", lhp.Start)
	if err != nil {
		log.Errorf("获取车道(pid:%s)前置车道失败,%s", lhp.Pid, err)
	}
	defer rows.Close()
	for rows.Next() {
		var pid string
		err = rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId(pid))
	}
	return ids
}

func GetLaneJunction(lhp *LaneHelper) *hdmap.Id {
	//可以从道路形态表（IBD_LINK_FORM）中获取道路交叉口数据FORM_OF_WAY=50
	row := db.QueryRow("SELECT b.object_pid FROM ibd_lane_link a , IBD_OBJECT_CROSS b WHERE contains(b.geometry,a.geometry) = 1 and a.lane_link_pid = ?", lhp.Pid)
	var pid string
	err := row.Scan(&pid)
	if err != nil {
		return MakeId("-1")
	}
	return MakeId(pid)
}

func GetLaneBoundaryIds(lhp *LaneHelper) (left string, right string) {

	//可以从道路形态表（IBD_LINK_FORM）中获取道路交叉口数据FORM_OF_WAY=50
	rows, err := db.Query("SELECT lane_boundary_pid,seq_num FROM IBD_LINK_BOUNDARY_REL WHERE link_pid = ?  ORDER BY seq_num;", lhp.Link)
	if err != nil {
		log.Errorf("获取道路(pid:%s)边界关系失败, 详情：%v", lhp.Link, err)
	}
	defer rows.Close()

	laneList := make(map[int]string)
	for rows.Next() {
		var pid string
		var seqnum int
		err = rows.Scan(&pid, &seqnum)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		laneList[seqnum] = pid
	}
	total := len(laneList)
	if total < 2 {
		log.Errorf("lane boundary number(%d) < 2 ~\n", total)
	}

	ok := false
	//使用车道中心线编号，推算车道边界，即车道编号为左边界，右边界+1即可
	left, ok = laneList[lhp.Seq]
	if !ok {
		log.Errorf("道路(%s)的车道(seq:%d)的左边界找不到~", lhp.Link, lhp.Seq)
	}

	rseq := lhp.Seq + 1
	if lhp.Seq == -1 {
		rseq = 1
	}
	right, ok = laneList[rseq]
	if !ok {
		log.Errorf("道路(%s)的车道(seq:%d)的右边界找不到~", lhp.Link, rseq)
	}

	return left, right
}

func IsBoundaryVirtual(boundarypid string) *bool {
	strSql := `SELECT boundary_type FROM ibd_lane_boundary WHERE lane_boundary_pid = ?;`
	rs := db.QueryRow(strSql, boundarypid)
	var tp int
	if err := rs.Scan(&tp); err != nil {
		log.Errorf("query lane boundary(pid:%s)'s virtual attributes error, details:%v", boundarypid, err)
	}
	virtual := tp == 1
	return &virtual
}

func MakeRoads(fast bool) []*hdmap.Road {
	roads := []*hdmap.Road{}
	rows, err := db.Query("SELECT link_pid from IBD_ROADLINK WHERE is_refline = 1;")
	if err != nil {
		log.Errorf("获取道路参考线失败, 详情:%s", err)
		return roads
	}
	links := []string{}
	for rows.Next() {
		var pid string
		err := rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		links = append(links, pid)
	}
	st := time.Now()
	total := len(links)
	bar := pb.StartNew(total).Prefix("making roads>") // create and start new bar
	if fast {
		roads = MakeRoadsFast(links, bar)
	} else {
		for _, link := range links {
			// st := time.Now()
			roads = append(roads, MakeRoad(link))
			bar.Increment()
			// fmt.Printf("progress(%d/%d) make road(pid:%s), time cost: %fs ~\n", i+1, total, link, time.Since(st).Seconds())
		}
	}
	bar.Finish() // finish bar
	fmt.Printf("make all roads(%d), time cost: %fs ~\n", len(links), time.Since(st).Seconds())
	return roads
}

func MakeRoadsFast(links []string, bar *pb.ProgressBar) []*hdmap.Road {
	// st := time.Now()
	roads := []*hdmap.Road{}
	type LaneList []*hdmap.Id
	linkLaneList := make(map[string]LaneList)
	//计算车道
	rows, err := db.Query("select link_pid,lane_link_pid from ibd_lane_link;")
	if err != nil {
		log.Errorf("获取道路的车道列表失败，详情：%s", err)
		return roads
	}
	defer rows.Close()
	for rows.Next() {
		var linkpid, lanepid string
		err = rows.Scan(&linkpid, &lanepid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		_, ok := linkLaneList[linkpid]
		if ok {
			linkLaneList[linkpid] = append(linkLaneList[linkpid], MakeId(lanepid))
		} else {
			linkLaneList[linkpid] = LaneList{MakeId(lanepid)}
		}
	}
	//计算边界
	rows2, err := db.Query(`SELECT a.link_pid,
								a.bseq,
								ST_AsBinary(Transform(SetSRID(a.bgeom, 4549), 32650)) as line,
								ST_AsBinary(
									Transform(SetSRID(StartPoint(a.bgeom), 4549), 32650)
								) as start,
								ST_Azimuth(ST_StartPoint(a.bgeom), ST_PointN(a.bgeom, 2)) as heading,
								GLENGTH(a.bgeom) as length,
								ST_Distance(
									ST_StartPoint(a.bgeom),
									ST_ClosestPoint(a.bgeom, ST_StartPoint(a.geometry))
								) as distance,
								ST_Azimuth(
									ST_StartPoint(a.bgeom),
									ST_ClosestPoint(a.bgeom, ST_StartPoint(a.geometry))
								) as startAz
							FROM (
									select t1.link_pid,
										t1.bseq,
										t1.bgeom,
										t2.geometry
									from aodx_lane_boundary t1
										left outer join ibd_roadlink t2 on t1.link_pid = t2.link_pid
								) a,
								(
									select link_pid,
										bseq,
										count(1) as cnt
									from aodx_lane_boundary
									group by link_pid,
										bseq
								) b
							where a.link_pid = b.link_pid
								and a.bseq = b.bseq
								and b.cnt = 1;`)
	if err != nil {
		log.Errorf("获取道路的边界列表失败，详情：%s", err)
		return roads
	}
	defer rows2.Close()
	type LinkBoundary struct {
		LSeq, RSeq int
		L, R       *hdmap.CurveSegment
	}
	linkBoundaryList := make(map[string]*LinkBoundary)
	for rows2.Next() {
		var linkpid string
		var bseq int
		var line wkb.LineString
		var start wkb.Point
		var heading, length, distance, startAz sql.NullFloat64
		err := rows2.Scan(&linkpid, &bseq, &line, &start, &heading, &length, &distance, &startAz)
		if err != nil {
			if err != sql.ErrNoRows {
				log.Errorf("make curve segment(linkpid:%s,bseq:%d) error, details:%s", linkpid, bseq, err)
			}
			continue
		}
		x, y, z := start.X(), start.Y(), start.Z()
		if !xyz {
			z = 0.0
		}
		startpos := hdmap.PointENU{
			X: &x,
			Y: &y,
			Z: &z,
		}
		points := []*hdmap.PointENU{}
		if line.Layout().ZIndex() != -1 {
			flat := line.FlatCoords()
			for i := 0; i < len(flat); i = i + line.Stride() {
				x, y, z := flat[i], flat[i+1], flat[i+2]
				if !xyz {
					z = 0.0
				}
				points = append(points, &hdmap.PointENU{
					X: &x,
					Y: &y,
					Z: &z,
				})
			}
		}
		s := 0.0
		h := 0.0
		l := 0.0
		if heading.Valid {
			h = heading.Float64
		}
		if length.Valid {
			l = length.Float64
		}

		if distance.Valid {
			s = distance.Float64
		}
		if startAz.Valid && heading.Valid {
			//同向为正，异向为负
			if math.Abs(startAz.Float64-heading.Float64) > math.Pi/2 {
				s = -s
			}
		} else {
			s = 0.0
		}

		curve := &hdmap.CurveSegment{
			CurveType: &hdmap.CurveSegment_LineSegment{
				LineSegment: &hdmap.LineSegment{
					Point: points,
				},
			},
			S:             &s,
			Heading:       &h,
			Length:        &l,
			StartPosition: &startpos,
		}
		_, ok := linkBoundaryList[linkpid]
		if ok {
			if bseq < linkBoundaryList[linkpid].LSeq {
				linkBoundaryList[linkpid].LSeq = bseq
				linkBoundaryList[linkpid].L = curve
			}
			if bseq > linkBoundaryList[linkpid].RSeq {
				linkBoundaryList[linkpid].RSeq = bseq
				linkBoundaryList[linkpid].R = curve
			}
		} else {
			linkBoundaryList[linkpid] = &LinkBoundary{
				LSeq: bseq,
				RSeq: bseq,
				L:    curve,
				R:    curve,
			}
		}
	}
	for _, link := range links {
		//构造roadsection
		linkBoundary := linkBoundaryList[link]
		leftEdge := &hdmap.BoundaryEdge{
			Curve: &hdmap.Curve{
				Segment: []*hdmap.CurveSegment{linkBoundary.L}, //通常segment也只有一段
			},
			Type: hdmap.BoundaryEdge_LEFT_BOUNDARY.Enum(),
		}
		rightEdge := &hdmap.BoundaryEdge{
			Curve: &hdmap.Curve{
				Segment: []*hdmap.CurveSegment{linkBoundary.R}, //通常segment也只有一段
			},
			Type: hdmap.BoundaryEdge_RIGHT_BOUNDARY.Enum(),
		}
		//当前section绝大多数情况也是只有左右两个边界，没有Hole
		outPoly := &hdmap.BoundaryPolygon{
			Edge: []*hdmap.BoundaryEdge{leftEdge, rightEdge},
		}
		boundary := &hdmap.RoadBoundary{
			OuterPolygon: outPoly,
			//Hole多数为空 -- cuidj 20211111
		}

		section := &hdmap.RoadSection{
			Id:       MakeId("1"), //暂定,编号从1开始
			LaneId:   linkLaneList[link],
			Boundary: boundary,
		}
		road := &hdmap.Road{
			Id:         MakeId(link),
			JunctionId: GetRoadJunction(link),
			Type:       hdmap.Road_CITY_ROAD.Enum(),
			Section:    []*hdmap.RoadSection{section}, //当前一般都是一条道路road对应一个section -- cuidj 2021111
		}
		roads = append(roads, road)
		bar.Increment()
	}
	// fmt.Printf("make all roads(%d) cost: %fs ~\n", len(roads), time.Since(st).Seconds())
	return roads
}

func MakeRoad(linkpid string) *hdmap.Road {
	//构造roadsection
	section := &hdmap.RoadSection{
		Id:       MakeId("1"), //暂定,编号从1开始
		LaneId:   GetRoadLaneIds(linkpid),
		Boundary: MakeRoadBoundary(linkpid),
	}
	return &hdmap.Road{
		Id:         MakeId(linkpid),
		JunctionId: GetRoadJunction(linkpid),
		Type:       hdmap.Road_CITY_ROAD.Enum(),
		Section:    []*hdmap.RoadSection{section}, //当前一般都是一条道路road对应一个section -- cuidj 2021111
	}
}

func GetRoadJunction(roadPid string) *hdmap.Id {
	//可以从道路形态表（IBD_LINK_FORM）中获取道路交叉口数据FORM_OF_WAY=50
	row := db.QueryRow("SELECT b.object_pid FROM IBD_ROADLINK a , IBD_OBJECT_CROSS b WHERE contains(b.geometry,a.geometry) = 1 and a.link_pid = ?", roadPid)
	var pid string
	err := row.Scan(&pid)
	if err != nil {
		return MakeId("-1")
	}
	return MakeId(pid)
}

func GetRoadLaneIds(linkpid string) []*hdmap.Id {
	laneIds := []*hdmap.Id{}
	rows, err := db.Query("select lane_link_pid from ibd_lane_link where link_pid = ?", linkpid)
	if err != nil {
		log.Errorf("获取道路(pid:%s)的车道列表失败，详情：%s", linkpid, err)
		return laneIds
	}
	defer rows.Close()

	for rows.Next() {
		var pid string
		err = rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		laneIds = append(laneIds, MakeId(pid))
	}

	return laneIds
}

func MakeRoadBoundary(linkpid string) *hdmap.RoadBoundary {
	//获取边界
	// st := time.Now()
	leftBpid, rightBpid := GetRoadBoundaryIds(linkpid)
	if leftBpid == "" || rightBpid == "" {
		log.Errorf("boundary pid is empty")
	}
	//处理左边界,正常道路边界只有一条不能重复
	left := MakeRoadBoundaryCurveSegment(linkpid, leftBpid)
	left.S = GetBoundaryStart(linkpid, leftBpid)
	leftEdge := &hdmap.BoundaryEdge{
		Curve: &hdmap.Curve{
			Segment: []*hdmap.CurveSegment{left}, //通常segment也只有一段
		},
		Type: hdmap.BoundaryEdge_LEFT_BOUNDARY.Enum(),
	}
	//处理右边界
	right := MakeRoadBoundaryCurveSegment(linkpid, rightBpid)
	right.S = GetBoundaryStart(linkpid, rightBpid)
	rightEdge := &hdmap.BoundaryEdge{
		Curve: &hdmap.Curve{
			Segment: []*hdmap.CurveSegment{right}, //通常segment也只有一段
		},
		Type: hdmap.BoundaryEdge_RIGHT_BOUNDARY.Enum(),
	}
	// fmt.Printf("road(pid:%s),time cost: %fs ~\n", linkpid, time.Since(st).Seconds())
	//当前section绝大多数情况也是只有左右两个边界，没有Hole
	outPoly := &hdmap.BoundaryPolygon{
		Edge: []*hdmap.BoundaryEdge{leftEdge, rightEdge},
	}
	return &hdmap.RoadBoundary{
		OuterPolygon: outPoly,
		//Hole多数为空 -- cuidj 20211111
	}
}

func GetRoadBoundaryIds(linkpid string) (left string, right string) {
	//可以从道路形态表（IBD_LINK_FORM）中获取道路交叉口数据FORM_OF_WAY=50
	rows, err := db.Query("SELECT lane_boundary_pid,seq_num FROM IBD_LINK_BOUNDARY_REL WHERE link_pid = ?  ORDER BY seq_num;", linkpid)
	if err != nil {
		log.Errorf("获取道路(pid:%s)边界失败, 详情:%s", linkpid, err)
	}
	defer rows.Close()

	pidlist := []string{}
	for rows.Next() {
		var pid string
		var seqnum int
		err = rows.Scan(&pid, &seqnum)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		pidlist = append(pidlist, pid)
	}
	total := len(pidlist)
	if total < 2 {
		log.Warnf("road(pid:%s)'s boundary number is %d ~\n", linkpid, total)
		return
	}
	return pidlist[0], pidlist[total-1]
}

func MakeLaneBoundaryCurveSegment(lanepid, boundarypid string) *hdmap.CurveSegment {
	if boundarypid == "" {
		return &hdmap.CurveSegment{}
	}
	r := db.QueryRow(`SELECT 
	ST_AsBinary(Transform(SetSRID(bgeom,4549),32650)) as line,
	ST_AsBinary(Transform(SetSRID(StartPoint(bgeom),4549),32650)) as start,
	ST_Azimuth(ST_StartPoint(bgeom),ST_PointN(bgeom,2)) as heading,
	GLENGTH(bgeom) as length
	FROM aodx_lane_boundary WHERE lane_link_pid = ? and lane_boundary_pid = ?;`, lanepid, boundarypid)
	var line wkb.LineString
	var start wkb.Point
	var heading, length sql.NullFloat64
	if err := r.Scan(&line, &start, &heading, &length); err != nil {
		if err != sql.ErrNoRows {
			log.Errorf("make curve segment(boundarypid:%s) error, details:%s", boundarypid, err)
		}
		return &hdmap.CurveSegment{}
	}

	x, y, z := start.X(), start.Y(), start.Z()
	if !xyz {
		z = 0.0
	}
	startpos := hdmap.PointENU{
		X: &x,
		Y: &y,
		Z: &z,
	}
	points := []*hdmap.PointENU{}
	if line.Layout().ZIndex() != -1 {
		flat := line.FlatCoords()
		for i := 0; i < len(flat); i = i + line.Stride() {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}
	s := 0.0
	h := 0.0
	l := 0.0
	if heading.Valid {
		h = heading.Float64
	}
	if length.Valid {
		l = length.Float64
	}
	return &hdmap.CurveSegment{
		CurveType: &hdmap.CurveSegment_LineSegment{
			LineSegment: &hdmap.LineSegment{
				Point: points,
			},
		},
		S:             &s,
		Heading:       &h,
		Length:        &l,
		StartPosition: &startpos,
	}
}

func MakeRoadBoundaryCurveSegment(linkpid, boundarypid string) *hdmap.CurveSegment {
	if boundarypid == "" {
		return &hdmap.CurveSegment{}
	}
	r := db.QueryRow(`SELECT 
	ST_AsBinary(Transform(SetSRID(bgeom,4549),32650)) as line,
	ST_AsBinary(Transform(SetSRID(StartPoint(bgeom),4549),32650)) as start,
	ST_Azimuth(ST_StartPoint(bgeom),ST_PointN(bgeom,2)) as heading,
	GLENGTH(bgeom) as length
	FROM aodx_lane_boundary WHERE link_pid = ? and lane_boundary_pid = ?;`, linkpid, boundarypid)
	var line wkb.LineString
	var start wkb.Point
	var heading, length sql.NullFloat64
	if err := r.Scan(&line, &start, &heading, &length); err != nil {
		if err != sql.ErrNoRows {
			log.Errorf("make curve segment(boundarypid:%s) error, details:%s", boundarypid, err)
		}
		return &hdmap.CurveSegment{}
	}
	x, y, z := start.X(), start.Y(), start.Z()
	if !xyz {
		z = 0.0
	}
	startpos := hdmap.PointENU{
		X: &x,
		Y: &y,
		Z: &z,
	}
	points := []*hdmap.PointENU{}
	if line.Layout().ZIndex() != -1 {
		flat := line.FlatCoords()
		for i := 0; i < len(flat); i = i + line.Stride() {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}
	s := 0.0
	h := 0.0
	l := 0.0
	if heading.Valid {
		h = heading.Float64
	}
	if length.Valid {
		l = length.Float64
	}
	return &hdmap.CurveSegment{
		CurveType: &hdmap.CurveSegment_LineSegment{
			LineSegment: &hdmap.LineSegment{
				Point: points,
			},
		},
		S:             &s,
		Heading:       &h,
		Length:        &l,
		StartPosition: &startpos,
	}
}

func GetBoundaryStart(linkpid, boundarypid string) *float64 {
	start := 0.0
	if boundarypid == "" {
		return &start
	}
	r := db.QueryRow(`SELECT 
	ST_Distance(ST_StartPoint(b.GEOMETRY),ST_ClosestPoint(b.geometry,ST_StartPoint(a.GEOMETRY))) as dist,
	ST_Azimuth(ST_StartPoint(b.GEOMETRY),ST_ClosestPoint(b.geometry,ST_StartPoint(a.GEOMETRY))) as saz,
	ST_Azimuth(ST_StartPoint(b.GEOMETRY),ST_PointN(b.geometry,2)) as baz
	FROM IBD_ROADLINK a, IBD_LANE_BOUNDARY b
	WHERE a.link_pid = ? and b.lane_boundary_pid = ?;`, linkpid, boundarypid)
	var distance float64
	var dist, saz, baz sql.NullFloat64
	if err := r.Scan(&dist, &saz, &baz); err != nil {
		log.Errorf("get boundary start error, details:%s", err)
		return &start
	}
	if dist.Valid {
		distance = dist.Float64
	}
	if saz.Valid && baz.Valid {
		//同向为正，异向为负
		if math.Abs(saz.Float64-baz.Float64) > math.Pi/2 {
			distance = -distance
		}
	} else {
		distance = 0.0
	}

	return &distance
}

func MakePncJunctions() []*hdmap.PNCJunction {
	junctions := []*hdmap.PNCJunction{}
	rows, err := db.Query("SELECT object_pid from ibd_object_cross;")
	if err != nil {
		log.Errorf("make junctions error, detail:%s", err)
	}
	pids := []string{}
	for rows.Next() {
		var pid string
		err := rows.Scan(&pid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		pids = append(pids, pid)
	}
	st := time.Now()
	total := len(pids)
	for _, pid := range pids {
		// st := time.Now()
		junctions = append(junctions, MakePncJunction(pid))
		// fmt.Printf("progress(%d/%d),make pncjunction(pid:%s), time cost: %fs ~\n", i+1, total, pid, time.Since(st).Seconds())
	}
	fmt.Printf("make all %d pncjunctions, time cost: %fs ~\n", total, time.Since(st).Seconds())
	return junctions
}

func MakePncJunction(junctionpid string) *hdmap.PNCJunction {
	sqlStr := `SELECT ST_AsBinary(transform(setsrid(geometry, 4549), 32650)) 
	FROM ibd_object_cross WHERE object_pid = ?;`
	rs := db.QueryRow(sqlStr, junctionpid)
	var poly wkb.Polygon
	if err := rs.Scan(&poly); err != nil {
		log.Errorf("rows scan error, details:%s", err)
		return &hdmap.PNCJunction{}
	}

	points := []*hdmap.PointENU{}
	if poly.Layout().ZIndex() != -1 {
		flat := poly.FlatCoords()
		for i := 0; i < len(flat); i = i + poly.Stride() {
			x, y, z := flat[i], flat[i+1], flat[i+2]
			if !xyz {
				z = 0.0
			}
			points = append(points, &hdmap.PointENU{
				X: &x,
				Y: &y,
				Z: &z,
			})
		}
	}

	pgroups := []*hdmap.PassageGroup{}
	entrGroups := GetPncJunctionEntranceGroups(junctionpid)
	pgroups = append(pgroups, entrGroups...)
	exitGroups := GetPncJunctionExitGroups(junctionpid)
	pgroups = append(pgroups, exitGroups...)

	return &hdmap.PNCJunction{
		Id: MakeId("PNC" + junctionpid),
		Polygon: &hdmap.Polygon{
			Point: points,
		},
		OverlapId:    GetPncJunctionOverlaps(junctionpid),
		PassageGroup: pgroups,
	}
}

func GetPncJunctionOverlaps(junctionpid string) []*hdmap.Id {
	ids := []*hdmap.Id{}
	//junction&lane
	laneSql := `select j.object_pid,l.lane_link_pid from ibd_object_cross j, ibd_lane_link l
				where contains(j.geometry,l.geometry) = 1 and j.object_pid = ?;`
	rows, err := db.Query(laneSql, junctionpid)
	if err != nil {
		log.Errorf("get junction's overlap lanes error, detail:%s", err)
		goto crosswalk
	}
	for rows.Next() {
		var jpid, lpid string
		err := rows.Scan(&jpid, &lpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId("PNC_Junction"+jpid+"_Lane"+lpid))
	}

crosswalk:
	walkSql := `select w.object_pid from ibd_object_cross c, ibd_object_cross_walk w
	where contains(c.geometry,w.geometry) = 1 and c.object_pid = ?;`
	rows, err = db.Query(walkSql, junctionpid)
	if err != nil {
		log.Errorf("get junction&crosswalk overlaps error, detail:%s", err)
		return ids
	}
	for rows.Next() {
		var cwpid string
		err := rows.Scan(&cwpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		ids = append(ids, MakeId("PNC_Junction"+junctionpid+"_CrossWalk"+cwpid))
	}
	//暂时不需要处理junction&signal的overlap直接返回
	return ids
}

func GetPncJunctionEntranceGroups(junctionpid string) []*hdmap.PassageGroup {
	pGroups := []*hdmap.PassageGroup{}
	//①获取所有进入道路，例如十字路口有四条进入道路
	//②获取每条进入路口道路的所有车道的后继车道为一组进入
	//③获取所有驶离道路，例如十字路口有四条驶离道路
	//④获取每条驶离路口道路的所有车道的前置车道为一组驶离

	//① ENTRANCE//所有进入路口的道路
	linkEntrSql := `SELECT A.link_pid
	FROM ibd_roadlink A,
		(
			SELECT r.s_node_pid AS s,
				j.geometry AS g
			FROM ibd_object_cross j,
				ibd_roadlink r
			WHERE j.object_pid = ?
				AND CONTAINS (j.geometry, r.geometry) = 1
		) v
	WHERE A.e_node_pid = v.s
		AND CONTAINS (v.g, A.geometry) = 0
	GROUP BY A.link_pid;`
	rows, err := db.Query(linkEntrSql, junctionpid)
	if err != nil {
		log.Errorf("get junction's overlap lanes error, detail:%s", err)
	}
	defer rows.Close()
	links := []string{}
	for rows.Next() {
		var linkpid string
		err := rows.Scan(&linkpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		links = append(links, linkpid)
	}

	for i, link := range links {
		laneSql := `SELECT lane_link_pid
		FROM ibd_lane_link
		WHERE s_lane_node_pid IN (
				SELECT e_lane_node_pid
				FROM ibd_lane_link
				WHERE link_pid = ?
			);`
		rows, err := db.Query(laneSql, link)
		if err != nil {
			log.Errorf("get junction&crosswalk overlaps error, detail:%s", err)
			continue
		}
		defer rows.Close()
		laneids := []*hdmap.Id{}
		for rows.Next() {
			var lpid string
			err := rows.Scan(&lpid)
			if err != nil {
				log.Errorf("rows scan error, details:%s", err)
				continue
			}
			laneids = append(laneids, MakeId(lpid))
		}

		passage := hdmap.Passage{
			Id:     MakeId("Passage_ENTRANCE" + strconv.Itoa(i+1)),
			LaneId: laneids,
			Type:   hdmap.Passage_ENTRANCE.Enum(),
		}

		enter := hdmap.PassageGroup{
			Id:      MakeId("PNC_Junction" + junctionpid + "_Link" + link),
			Passage: []*hdmap.Passage{&passage},
		}
		pGroups = append(pGroups, &enter)
	}
	//还要再处理驶离的
	return pGroups
}

func GetPncJunctionExitGroups(junctionpid string) []*hdmap.PassageGroup {
	pGroups := []*hdmap.PassageGroup{}
	//①获取所有进入道路，例如十字路口有四条进入道路
	//②获取每条进入路口道路的所有车道的后继车道为一组进入
	//③获取所有驶离道路，例如十字路口有四条驶离道路
	//④获取每条驶离路口道路的所有车道的前置车道为一组驶离

	//③ EXIT//所有驶离路口的道路
	linkEntrSql := `SELECT A.link_pid
	FROM ibd_roadlink A,
		(
			SELECT r.e_node_pid AS e,
				j.geometry AS g
			FROM ibd_object_cross j,
				ibd_roadlink r
			WHERE j.object_pid = ?
				AND CONTAINS (j.geometry, r.geometry) = 1
		) v
	WHERE A.s_node_pid = v.e
		AND CONTAINS (v.g, A.geometry) = 0
	GROUP BY A.link_pid;`
	rows, err := db.Query(linkEntrSql, junctionpid)
	if err != nil {
		log.Errorf("get junction's overlap lanes error, detail:%s", err)
	}
	defer rows.Close()
	links := []string{}
	for rows.Next() {
		var linkpid string
		err := rows.Scan(&linkpid)
		if err != nil {
			log.Errorf("rows scan error, details:%s", err)
			continue
		}
		links = append(links, linkpid)
	}

	for i, link := range links {
		laneSql := `SELECT lane_link_pid
		FROM ibd_lane_link
		WHERE e_lane_node_pid IN (
				SELECT s_lane_node_pid
				FROM ibd_lane_link
				WHERE link_pid = ?
			);`
		rows, err := db.Query(laneSql, link)
		if err != nil {
			log.Errorf("get junction&crosswalk overlaps error, detail:%s", err)
			continue
		}
		defer rows.Close()
		laneids := []*hdmap.Id{}
		for rows.Next() {
			var lpid string
			err := rows.Scan(&lpid)
			if err != nil {
				log.Errorf("rows scan error, details:%s", err)
				continue
			}
			laneids = append(laneids, MakeId(lpid))
		}

		passage := hdmap.Passage{
			Id:     MakeId("Passage_EXIT" + strconv.Itoa(i+1)),
			LaneId: laneids,
			Type:   hdmap.Passage_EXIT.Enum(),
		}

		enter := hdmap.PassageGroup{
			Id:      MakeId("PNC_Junction" + junctionpid + "_Link" + link),
			Passage: []*hdmap.Passage{&passage},
		}
		pGroups = append(pGroups, &enter)
	}
	//还要再处理驶离的
	return pGroups
}

func MakeId(id string) *hdmap.Id {
	return &hdmap.Id{
		Id: &id,
	}
}

func GetLayerExtent(table string, srid, target int) (l, b, r, t float64) {
	rs := db.QueryRow(`SELECT ST_AsBinary(Transform(SetSrid(GetLayerExtent(?),?),?));`, table, srid, target)
	var ext wkb.Polygon
	if err := rs.Scan(&ext); err != nil {
		log.Errorf("获取数据(%s)范围失败, Error: %v", table, err)
	}
	l = ext.Bounds().Min(0)
	b = ext.Bounds().Min(1)
	r = ext.Bounds().Max(0)
	t = ext.Bounds().Max(1)
	return l, b, r, t
}

func AodxCreate() error {
	sqlStr := `CREATE TABLE IF NOT EXISTS aodx_lane_boundary AS
	SELECT x1.link_pid AS link_pid,
		x1.lane_link_pid AS lane_link_pid,
		x2.lane_boundary_pid AS lane_boundary_pid,
		x1.seq_num AS lseq,
		x2.seq_num AS bseq,
		0 AS ver,
		st_addmeasure(x1.geometry, 0, st_length(x1.geometry)) AS geom,
		x2.geometry AS bgeom
	FROM ibd_lane_link x1,
		(
			SELECT a.LINK_PID,
				a.LANE_BOUNDARY_PID,
				a.SEQ_NUM,
				b.lane_boundary_pid,
				b.geometry
			FROM "IBD_LINK_BOUNDARY_REL" AS a,
				"ibd_lane_boundary" AS b
			WHERE b.lane_boundary_pid = a.LANE_BOUNDARY_PID
		) AS x2
	WHERE x1.link_pid = x2.link_pid AND (
		(x1.seq_num != -1 AND x2.seq_num <= x1.seq_num + 1 AND x2.seq_num >= x1.seq_num) OR
		(x1.seq_num == -1 AND x2.seq_num <= 1 AND x2.seq_num >= -1));`

	_, err := db.Exec(sqlStr)
	if err != nil {
		return err
	}
	sqlStr = `CREATE TABLE IF NOT EXISTS aodx_lane_link AS
				SELECT link_pid,lane_link_pid,lseq,geom FROM aodx_lane_boundary GROUP BY lane_link_pid;`
	_, err = db.Exec(sqlStr)
	if err != nil {
		return err
	}
	return nil
}

func UpdateSRID(srid int) error {
	//需要先设置参考系，再恢复空间字段，
	tx, err := db.Begin()
	if err != nil {
		log.Fatal(err)
	}
	stmt, err := tx.Prepare(`UPDATE "aodx_lane_boundary" SET "geom" = SetSrid("geom", ?);`)
	if err != nil {
		log.Fatal(err)
	}

	stmtb, err := tx.Prepare(`UPDATE "aodx_lane_boundary" SET "bgeom" = SetSrid("bgeom", ?);`)
	if err != nil {
		log.Fatal(err)
	}
	// sqlStr := `UPDATE "aodx_lane_boundary" SET "geom" = SetSrid("geom", ?)`
	_, err = stmt.Exec(srid)
	if err != nil {
		log.Error(err)
		return err
	}
	// sqlStr = `UPDATE "aodx_lane_boundary" SET "bgeom" = SetSrid("bgeom", ?)`
	_, err = stmtb.Exec(srid)
	if err != nil {
		log.Error(err)
		return err
	}
	err = tx.Commit()
	if err != nil {
		return err
	}
	// sqlStr := `SELECT RecoverGeometryColumn('aodx_lane_boundary', 'geom', ?, 'LINESTRING', 'XYZM');`
	// _, err = db.Exec(sqlStr, srid)
	// if err != nil {
	// 	log.Error(err)
	// 	return err
	// }
	// sqlStr = `SELECT RecoverGeometryColumn('aodx_lane_boundary', 'bgeom', ?, 'LINESTRING', 'XYZ');`
	// _, err = db.Exec(sqlStr, srid)
	// if err != nil {
	// 	log.Error(err)
	// 	return err
	// }
	return nil
}

func UpdateAodxInterBoundaryOffset() error {
	//分四种情况来处理, az>0/az<0 & left/right C22
	// 获取所有需要处理的车道和边界
	rows, err := db.Query(`SELECT link_pid,lane_link_pid,lane_boundary_pid,lseq,bseq,
								  azimuth(startpoint(bgeom),endpoint(bgeom)) - azimuth(startpoint(geom),endpoint(geom)) as az 
						   FROM aodx_lane_boundary where intersects(geom,bgeom);`)
	if err != nil {
		log.Errorf("query interbounday lanes error, details:%v", err)
		return err
	}
	defer rows.Close()

	llbs := []LLB{}
	for rows.Next() {
		llb := LLB{}
		err = rows.Scan(&llb.Link, &llb.Lane, &llb.Boundary, &llb.LSeq, &llb.BSeq, &llb.Az)
		if err != nil {
			log.Errorf("llb rows scan error, details:%s", err)
			continue
		}
		if llb.Az > math.Pi {
			llb.Az = llb.Az - 2*math.Pi
		}
		if llb.Az < -math.Pi {
			llb.Az = llb.Az - 2*math.Pi
		}
		llbs = append(llbs, llb)
	}

	for _, llb := range llbs {
		//测试有且只有一条数据
		//select count(*) from aodx_lane_boundary where link_pid = '2021110511172788036' and lseq = 3-1 and bseq = 3-1
		postion := 0
		if llb.LSeq == llb.BSeq {
			postion = -1
			//左边界，找到左边的端点往左移动，将另一个点连接
			err := BoundaryTest(&llb, -1)
			if err != nil {
				log.Errorf("lane(pid:%s) boundary test error, details:%v", llb.Lane, err)
				continue
			}
			if llb.Az < 0 {
				// az < 0 则终点在左侧,translate to endpoint,link next left boundary's startpoint
				//要找到左侧非共点（起点）边界
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
				ST_AddPoint(
					ST_AddPoint(
						ST_OffsetCurve(geom, ST_Distance(endpoint(bgeom), geom)),
						(
							SELECT startpoint(la.bgeom)
							FROM aodx_lane_boundary la,
								(
									SELECT bgeom
									FROM aodx_lane_boundary
									WHERE lane_link_pid = ?
										AND bseq = ?
								) lb
							WHERE la.link_pid = ?
								AND la.lseq < ?
								AND la.bseq < ?
								AND intersects(la.bgeom, lb.bgeom) = 0
							ORDER BY la.bseq DESC
							LIMIT 1), 0),
					endpoint(bgeom), -1)),
				ver = 2
			WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			} else {
				// az > 0 则起点在左侧,translate to startpoint,link next left boundary's endpoint
				//要找到左侧非共点（终点）边界
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
				ST_AddPoint(
					ST_AddPoint(
						ST_OffsetCurve(geom, ST_Distance(startpoint(bgeom), geom)),
						(SELECT endpoint(la.bgeom)
								FROM aodx_lane_boundary la,
									(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
									) lb
								WHERE la.link_pid = ?
									AND la.lseq < ?
									AND la.bseq < ?
									AND intersects(la.bgeom, lb.bgeom) = 0
								ORDER BY la.bseq DESC
								LIMIT 1), -1), 
					startpoint(bgeom),0)),
					ver = 2
				WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			}
		} else if llb.LSeq == llb.BSeq-1 {
			postion = 1
			//右边界，找到右边的端点往右移动，将另一个点连接
			err := BoundaryTest(&llb, 1)
			if err != nil {
				log.Errorf("lane(pid:%s) boundary test error, details:%v", llb.Lane, err)
				continue
			}
			if llb.Az < 0 {
				// az < 0 则起点在右侧,translate to startpoint,link next right boundary's endpoint
				//确认非共点
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
				ST_AddPoint(
					ST_AddPoint(
						ST_OffsetCurve(geom, -ST_Distance(startpoint(bgeom), geom)),
						(SELECT endpoint(la.bgeom)
								FROM aodx_lane_boundary la,
									(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
									) lb
								WHERE la.link_pid = ?
									AND la.lseq > ?
									AND la.bseq > ?
									AND intersects(la.bgeom, lb.bgeom) = 0
								ORDER BY la.bseq
								LIMIT 1), -1),
					startpoint(bgeom))),
					ver = 2
				WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			} else {
				// baz-laz > 0 则终点在右侧,translate to endpoint,link next right boundary's startpoint
				//需确认非共点
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
				ST_AddPoint(
					ST_AddPoint(
						ST_OffsetCurve(geom, -ST_Distance(endpoint(bgeom), geom)),
						(SELECT startpoint(la.bgeom)
							FROM aodx_lane_boundary la,
								(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
								) lb
							WHERE la.link_pid = ?
									AND la.lseq > ?
									AND la.bseq > ?
									AND intersects(la.bgeom, lb.bgeom) = 0
							ORDER BY la.bseq
							LIMIT 1), -1),
					endpoint(bgeom), 0)),
					ver = 2
					WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			}
		} else {
			log.Errorf("this should not enter, detail: %d ~", postion) //log error
		}
	}
	return nil
}

func UpdateAodxInterBoundaryTran() error {
	//分四种情况来处理, az>0/az<0 & left/right C22
	// 获取所有需要处理的车道和边界
	rows, err := db.Query(`SELECT link_pid,lane_link_pid,lane_boundary_pid,lseq,bseq,
								  azimuth(startpoint(bgeom),endpoint(bgeom)) - azimuth(startpoint(geom),endpoint(geom)) as az 
						   FROM aodx_lane_boundary where intersects(geom,bgeom);`)
	if err != nil {
		log.Errorf("query interbounday lanes error, details:%v", err)
		return err
	}
	defer rows.Close()

	llbs := []LLB{}
	for rows.Next() {
		llb := LLB{}
		err = rows.Scan(&llb.Link, &llb.Lane, &llb.Boundary, &llb.LSeq, &llb.BSeq, &llb.Az)
		if err != nil {
			log.Errorf("llb rows scan error, details:%s", err)
			continue
		}

		if llb.Az > math.Pi {
			llb.Az = llb.Az - 2*math.Pi
		}
		if llb.Az < -math.Pi {
			llb.Az = llb.Az - 2*math.Pi
		}

		llbs = append(llbs, llb)
	}

	for _, llb := range llbs {
		//测试有且只有一条数据
		postion := 0
		if llb.LSeq == llb.BSeq {
			postion = -1
			//左边界，找到左边的端点往左移动，将另一个点连接
			err := BoundaryTest(&llb, -1)
			if err != nil {
				log.Errorf("lane(pid:%s) boundary test error, details:%v", llb.Lane, err)
				continue
			}
			if llb.Az < 0 {
				// az < 0 则终点在左侧,translate to endpoint,link next left boundary's startpoint
				//要找到左侧非共点（起点）边界
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
							SetPoint(
								ST_Translate(
									geom,
									st_x(endpoint(bgeom)) - st_x(endpoint(geom)),
									st_y(endpoint(bgeom)) - st_y(endpoint(geom)),
									st_z(endpoint(bgeom)) - st_z(endpoint(geom))
								),
								0,
								(SELECT startpoint (la.bgeom)
								FROM aodx_lane_boundary la,
									(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
									) lb
								WHERE la.link_pid = ?
									AND la.lseq < ?
									AND la.bseq < ?
									AND intersects (la.bgeom, lb.bgeom) = 0
								ORDER BY la.bseq DESC
								LIMIT 1)
							)),
							ver = 1
						WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			} else {
				// az > 0 则起点在左侧,translate to startpoint,link next left boundary's endpoint
				//要找到左侧非共点（终点）边界
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
					SetPoint(
						ST_Translate(
							geom,
							st_x(startpoint(bgeom)) - st_x(startpoint(geom)),
							st_y(startpoint(bgeom)) - st_y(startpoint(geom)),
							st_z(startpoint(bgeom)) - st_z(startpoint(geom))
						),
						numpoints(geom)-1,
						(SELECT endpoint(la.bgeom)
								FROM aodx_lane_boundary la,
									(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
									) lb
								WHERE la.link_pid = ?
									AND la.lseq < ?
									AND la.bseq < ?
									AND intersects(la.bgeom, lb.bgeom) = 0
								ORDER BY la.bseq DESC
								LIMIT 1)
					)),
					ver = 1
				WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			}
		} else if llb.LSeq == llb.BSeq-1 {
			postion = 1
			//右边界，找到右边的端点往右移动，将另一个点连接
			err := BoundaryTest(&llb, 1)
			if err != nil {
				log.Errorf("lane(pid:%s) boundary test error, details:%v", llb.Lane, err)
				continue
			}
			if llb.Az < 0 {
				// az < 0 则起点在右侧,translate to startpoint,link next right boundary's endpoint
				//确认非共点
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
					SetPoint(
						ST_Translate(
							geom,
							st_x(startpoint(bgeom)) - st_x(startpoint(geom)),
							st_y(startpoint(bgeom)) - st_y(startpoint(geom)),
							st_z(startpoint(bgeom)) - st_z(startpoint(geom))
						),
						numpoints(geom)-1,
						(SELECT endpoint(la.bgeom)
								FROM aodx_lane_boundary la,
									(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
									) lb
								WHERE la.link_pid = ?
									AND la.lseq > ?
									AND la.bseq > ?
									AND intersects(la.bgeom, lb.bgeom) = 0
								ORDER BY la.bseq
								LIMIT 1)
					)),
					ver = 1
				WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			} else {
				// baz-laz > 0 则终点在右侧,,translate to endpoint,link next right boundary's startpoint
				//需确认非共点
				upSql := `UPDATE aodx_lane_boundary SET bgeom = CastToXYZ(
							SetPoint(
								ST_Translate(
									geom,
									st_x(endpoint(bgeom)) - st_x(endpoint(geom)),
									st_y(endpoint(bgeom)) - st_y(endpoint(geom)),
									st_z(endpoint(bgeom)) - st_z(endpoint(geom))
								),
								0,
								(SELECT startpoint(la.bgeom)
								FROM aodx_lane_boundary la,
									(
										SELECT bgeom
										FROM aodx_lane_boundary
										WHERE lane_link_pid = ?
											AND bseq = ?
									) lb
								WHERE la.link_pid = ?
									AND la.lseq > ?
									AND la.bseq > ?
									AND intersects(la.bgeom, lb.bgeom) = 0
								ORDER BY la.bseq
								LIMIT 1)
							)),
							ver = 1
						WHERE lane_link_pid = ? and bseq = ?;`
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := db.Exec(upSql, llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
			}
		} else {
			log.Errorf("this should not enter, detail: %d ~", postion) //log error
		}
	}
	return nil
}

func UpdateAodxInterBoundaryCurve() error {
	//分四种情况来处理, az>0/az<0 & left/right C22
	// 获取所有需要处理的车道和边界
	rows, err := db.Query(`SELECT link_pid,lane_link_pid,lane_boundary_pid,lseq,bseq,
								  azimuth(startpoint(bgeom),endpoint(bgeom)) - azimuth(startpoint(geom),endpoint(geom)) as az 
						   FROM aodx_lane_boundary where intersects(geom,bgeom);`)
	if err != nil {
		log.Errorf("query interbounday lanes error, details:%v", err)
		return err
	}
	defer rows.Close()

	llbs := []LLB{}
	for rows.Next() {
		llb := LLB{}
		err = rows.Scan(&llb.Link, &llb.Lane, &llb.Boundary, &llb.LSeq, &llb.BSeq, &llb.Az)
		if err != nil {
			log.Errorf("llb rows scan error, details:%s", err)
			continue
		}
		if llb.Az > math.Pi {
			llb.Az = llb.Az - 2*math.Pi
		}
		if llb.Az < -math.Pi {
			llb.Az = llb.Az - 2*math.Pi
		}
		llbs = append(llbs, llb)
	}
	total := len(llbs)
	st := time.Now()
	tx, err := db.Begin()
	if err != nil {
		log.Errorf("update aodx interboundaries begin error, details:%v", err)
		return err
	}

	stmt1, err := tx.Prepare(`UPDATE aodx_lane_boundary SET 
					bgeom = (
						SELECT AddPoint(AddPoint(MakeLine(startpoint(la.bgeom),Line_Interpolate_Point(la.bgeom,0.001)),Line_Interpolate_Point(lb.bgeom,0.999),-1),endpoint(lb.bgeom),-1)
						FROM aodx_lane_boundary la,
							(
								SELECT bgeom
								FROM aodx_lane_boundary
								WHERE lane_link_pid = ?
									AND bseq = ?
							) lb
						WHERE la.link_pid = ?
							AND la.lseq < ?
							AND la.bseq < ?
							AND intersects (la.bgeom, lb.bgeom) = 0
						ORDER BY la.bseq DESC
						LIMIT 1),
					ver = 31
					WHERE lane_link_pid = ? and bseq = ?;`)
	if err != nil {
		log.Fatal(err)
	}
	defer stmt1.Close()

	stmt2, err := tx.Prepare(`UPDATE aodx_lane_boundary SET 
					bgeom =	(
						SELECT AddPoint(AddPoint(MakeLine(startpoint(lb.bgeom),Line_Interpolate_Point(lb.bgeom,0.001)),Line_Interpolate_Point(la.bgeom,0.999),-1),endpoint(la.bgeom),-1)
						FROM aodx_lane_boundary la,
							(
								SELECT bgeom
								FROM aodx_lane_boundary
								WHERE lane_link_pid = ?
									AND bseq = ?
							) lb
						WHERE la.link_pid = ?
							AND la.lseq < ?
							AND la.bseq < ?
							AND intersects (la.bgeom, lb.bgeom) = 0
						ORDER BY la.bseq DESC
						LIMIT 1),
						ver = 32
					WHERE lane_link_pid = ? and bseq = ?;`)
	if err != nil {
		log.Fatal(err)
	}
	defer stmt2.Close()

	stmt3, err := tx.Prepare(`UPDATE aodx_lane_boundary SET 
					bgeom = (
						SELECT AddPoint(AddPoint(MakeLine(startpoint(la.bgeom),Line_Interpolate_Point(la.bgeom,0.001)),Line_Interpolate_Point(lb.bgeom,0.999),-1),endpoint(lb.bgeom),-1)
						FROM aodx_lane_boundary la,
							(
								SELECT bgeom
								FROM aodx_lane_boundary
								WHERE lane_link_pid = ?
									AND bseq = ?
							) lb
						WHERE la.link_pid = ?
							AND la.lseq > ?
							AND la.bseq > ?
							AND intersects(la.bgeom, lb.bgeom) = 0
						ORDER BY la.bseq
						LIMIT 1),
					ver = 33
					WHERE lane_link_pid = ? and bseq = ?;`)
	if err != nil {
		log.Fatal(err)
	}
	defer stmt3.Close()

	stmt4, err := tx.Prepare(`UPDATE aodx_lane_boundary SET 
					bgeom = (
						SELECT AddPoint(AddPoint(MakeLine(startpoint(lb.bgeom),Line_Interpolate_Point(lb.bgeom,0.001)),Line_Interpolate_Point(la.bgeom,0.999),-1),endpoint(la.bgeom),-1)
						FROM aodx_lane_boundary la,
							(
								SELECT bgeom
								FROM aodx_lane_boundary
								WHERE lane_link_pid = ?
									AND bseq = ?
							) lb
						WHERE la.link_pid = ?
							AND la.lseq > ?
							AND la.bseq > ?
							AND intersects(la.bgeom, lb.bgeom) = 0
						ORDER BY la.bseq
						LIMIT 1),
					ver = 34
					WHERE lane_link_pid = ? and bseq = ?;`)
	if err != nil {
		log.Fatal(err)
	}
	defer stmt4.Close()

	for i, llb := range llbs {
		//测试有且只有一条数据
		// st := time.Now()
		//select count(*) from aodx_lane_boundary where link_pid = '2021110511172788036' and lseq = 3-1 and bseq = 3-1
		postion := 0
		if llb.LSeq == llb.BSeq {
			postion = -1
			if llb.Az < 0 {
				// az < 0 则终点在左侧,translate to endpoint,link next left boundary's startpoint
				//要找到左侧非共点（起点）边界
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := stmt1.Exec(llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
				// log.Infof("1 update aodx lane(pid:%s) intersect boundary ~", llb.Lane)
			} else {
				// az > 0 则起点在左侧,translate to startpoint,link next left boundary's endpoint
				//要找到左侧非共点（终点）边界
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := stmt2.Exec(llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
				// log.Infof("2 update aodx lane(pid:%s) intersect boundary ~", llb.Lane)
			}
		} else if llb.LSeq == llb.BSeq-1 {
			postion = 1
			//右边界，找到右边的端点往右移动，将另一个点连接
			if llb.Az < 0 {
				// az < 0 则起点在右侧,translate to startpoint,link next right boundary's endpoint
				//确认非共点
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := stmt3.Exec(llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
				// log.Infof("3 update aodx lane(pid:%s) intersect boundary ~", llb.Lane)
			} else {
				// baz-laz > 0 则终点在右侧,,translate to endpoint,link next right boundary's startpoint
				//需确认非共点
				//然后找到该左边界左侧相邻边界的startpoint，addto line start
				_, err := stmt4.Exec(llb.Lane, llb.BSeq, llb.Link, llb.LSeq, llb.BSeq, llb.Lane, llb.BSeq)
				if err != nil {
					log.Errorf("update aodx lane(pid:%s) intersect boundary error, details:%v", llb.Lane, err)
					continue
				}
				// log.Infof("4 update aodx lane(pid:%s) intersect boundary ~", llb.Lane)
			}
		} else {
			log.Errorf("this should not enter, detail: %d ~", postion) //log error
			continue
		}
		log.Infof("progress(%d/%d), update road(pid:%s) lane(pid:%s) boundary(pid:%s,seq:%d) completed, time cost: %fs ~\n", i+1, total, llb.Link, llb.Lane, llb.Boundary, llb.BSeq, time.Since(st).Seconds())
	}
	err = tx.Commit()
	if err != nil {
		return err
	}
	log.Infof("update all %d boundary completed, time cost:%fs ~\n", total, time.Since(st).Seconds())
	return nil
}

//BoundaryTest 测试左左/右右车道边界是正常, postion=-1 → 左,postion=1 → 右
func BoundaryTest(llb *LLB, postion int) error {
	tSql := `select count(*) from aodx_lane_boundary 
	where link_pid = ? and lseq = ? and bseq = ?`
	rs := db.QueryRow(tSql, llb.Link, llb.LSeq+postion, llb.BSeq+postion)
	var cnt int
	if err := rs.Scan(&cnt); err != nil {
		return err
	}
	if cnt == 1 {
		return nil
	}
	return fmt.Errorf("the boundary number is not 1, details:%d~", cnt)
}

func InsertEpsgSrids(srid int) error {

	row := db.QueryRow("select srid from spatial_ref_sys where srid = ?;", srid)
	var s int
	if err := row.Scan(&s); err != nil {
		if err == sql.ErrNoRows {
			_, err := db.Exec("SELECT InsertEpsgSrid(?);", srid)
			if err != nil {
				log.Errorf("InsertEpsgSrid(%d) failed, details: %v", srid, err)
				return err
			}
			return nil
		}
		log.Errorf("get srids failed, details: %v", err)
	}
	log.Warnf("the srid(%d) already exists ~", srid)
	return nil
}

func CaclPoly3C(pt1, pt2 Point3D, dir1, dir2 PtVector) *Poly3C {

	distR1, distR2 := 1.0, 1.0
	// th := math.Acos(dir1.X*dir2.X + dir1.Y*dir2.Y)
	// log.Infof("math.Acos → %f\n", th)
	// if math.Abs(th) < math.Pi/6 || math.Abs(th) > math.Pi*5/6 {
	// 	distR1 = 1
	// 	distR2 = 1
	// }

	dx := pt2.X - pt1.X
	dy := pt2.Y - pt1.Y

	if (dir1.X == 0 && dir2.X == 0 && dx == 0) && (dir1.Y > 0 && dir2.Y > 0 && dy > 0 || dir1.Y < 0 && dir2.Y < 0 && dy < 0) ||
		(dir1.Y == 0 && dir2.Y == 0 && dy == 0) && (dir1.X > 0 && dir2.X > 0 && dx > 0 || dir1.X < 0 && dir2.X < 0 && dx < 0) ||
		(dir1.X != 0 && dir2.X != 0 && dx != 0) && ((dir1.Y/dir1.X) == (dir2.Y/dir2.X) && (dir1.Y/dir1.X) == (dy/dx)) {
		return &Poly3C{}
	}

	t1 := math.Atan2(dy, dx)
	t2 := math.Atan2(dir1.Y, dir1.X)
	a1, b1, c1, d1 := 1.0, 1.0, 1.0, 1.0
	a2, b2, c2, d2 := 1.0, 1.0, 1.0, 1.0
	a1 = pt1.X
	a2 = pt1.Y
	dt1 := math.Abs(t2 - t1)
	dist := math.Sqrt(dx*dx + dy*dy)
	if dt1 == 0.5*math.Pi || dt1 == 1.5*math.Pi {
		return &Poly3C{}
	} else {
		b1 = distR1 * math.Cos(t2) / math.Cos(t2-t1)
		b2 = distR1 * math.Sin(t2) / math.Cos(t2-t1)
	}

	t3 := math.Atan2(dir2.Y, dir2.X)
	dt2 := math.Abs(t3 - t1)
	if dt2 == 0.5*math.Pi || dt2 == 1.5*math.Pi {
		return &Poly3C{}
	} else {

		kx2 := distR2 * math.Cos(t3) / math.Cos(t3-t1)
		ky2 := distR2 * math.Sin(t3) / math.Cos(t3-t1)

		c1 = 3*(pt2.X-a1)/math.Pow(dist, 2) - (kx2+2*b1)/dist
		d1 = 2*(a1-pt2.X)/math.Pow(dist, 3) + (b1+kx2)/math.Pow(dist, 2)

		c2 = 3*(pt2.Y-a2)/math.Pow(dist, 2) - (ky2+2*b2)/dist
		d2 = 2*(a2-pt2.Y)/math.Pow(dist, 3) + (b2+ky2)/math.Pow(dist, 2)
	}

	return &Poly3C{
		Au:     a1,
		Bu:     b1,
		Cu:     c1,
		Du:     d1,
		Av:     a2,
		Bv:     b2,
		Cv:     c2,
		Dv:     d2,
		Length: dist,
	}
}

func CreateCurveLine(poly3c *Poly3C, interval float64) *CurveLine {
	cl := CurveLine{}
	for i := 0.0; i < poly3c.Length; i = i + interval {
		x := poly3c.Au + poly3c.Bu*i + poly3c.Cu*i*i + poly3c.Du*i*i*i
		y := poly3c.Av + poly3c.Bv*i + poly3c.Cv*i*i + poly3c.Dv*i*i*i
		pt := Point3D{
			X: x,
			Y: y,
		}
		cl = append(cl, &pt)
	}
	return &cl
}

func UpdateAodxInterBoundaryCurve2() error {
	// 获取所有需要处理的车道边界,使用lanepid和bseq
	rows, err := db.Query(`SELECT lane_link_pid,bseq,ST_AsBinary(bgeom)
						   FROM aodx_lane_boundary 
						   WHERE ver > 30;`)
	if err != nil {
		log.Errorf("query boundary for curve error, details:%v", err)
		return err
	}
	defer rows.Close()

	llbs := []LLB{}
	lines := []*wkb.LineString{}
	for rows.Next() {
		llb := LLB{}
		var line wkb.LineString
		err = rows.Scan(&llb.Lane, &llb.BSeq, &line)
		if err != nil {
			log.Errorf("lane(pid:%s) boundary(seq:%d) rows scan error, details:%s", llb.Lane, llb.BSeq, err)
			continue
		}
		llbs = append(llbs, llb)
		lines = append(lines, &line)
	}
	st := time.Now()

	tx, err := db.Begin()
	if err != nil {
		log.Errorf("fitting aodx interboundaries begin error, details:%v", err)
		return err
	}

	stmt, err := tx.Prepare(`UPDATE aodx_lane_boundary SET bgeom = ST_GeomFromWKB(?)
					WHERE lane_link_pid = ? and bseq = ?;`)
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()
	total := len(llbs)
	for i, llb := range llbs {
		// st := time.Now()
		//计算相关参数
		line := lines[i]
		pts := []Point3D{}

		if line.Layout().ZIndex() != -1 {
			flat := line.FlatCoords()
			for i := 0; i < len(flat); i = i + line.Stride() {
				x, y, z := flat[i], flat[i+1], flat[i+2]
				if !xyz {
					z = 0.0
				}
				pts = append(pts, Point3D{
					X: x,
					Y: y,
					Z: z,
				})
			}
		}
		pt1 := pts[0]
		pt2 := pts[1]
		pt3 := pts[2]
		pt4 := pts[3]

		ddx := pt2.X - pt1.X
		ddy := pt2.Y - pt1.Y
		dxy := math.Sqrt(ddx*ddx + ddy*ddy)

		dir1 := PtVector{
			X: ddx / dxy,
			Y: ddy / dxy,
		}
		ddx = pt4.X - pt3.X
		ddy = pt4.Y - pt3.Y
		dxy = math.Sqrt(ddx*ddx + ddy*ddy)
		dir2 := PtVector{
			X: ddx / dxy,
			Y: ddy / dxy,
		}
		poly3c := CaclPoly3C(pt1, pt4, dir1, dir2)
		if poly3c.Length == 0 {
			continue
		}

		// log.Infof("%v\n", poly3c)
		cline := CreateCurveLine(poly3c, 0.5)
		crds := []float64{}
		for _, pt := range *cline {
			crds = append(crds, []float64{pt.X, pt.Y, pt1.Z}...)
		}
		// log.Infof("%v\n", crds)

		wkbLine := wkb.LineString{LineString: geom.NewLineStringFlat(geom.XYZ, crds)}
		_, err := stmt.Exec(&wkbLine, llb.Lane, llb.BSeq)
		if err != nil {
			log.Errorf("fitting aodx lane(pid:%s) boundary error, details:%v", llb.Lane, err)
			continue
		}
		log.Infof("progress(%d/%d), update road(pid:%s) lane(pid:%s) boundary(pid:%s,seq:%d) completed, time cost: %fs ~\n", i+1, total, llb.Link, llb.Lane, llb.Boundary, llb.BSeq, time.Since(st).Seconds())
	}
	err = tx.Commit()
	if err != nil {
		return err
	}
	log.Infof("fitting all %d boundary completed, time cost:%fs ~\n", total, time.Since(st).Seconds())
	return nil
}

func GetLaneType(ltype int32) *hdmap.Lane_LaneType {
	//	Lane_NONE         Lane_LaneType = 1
	// Lane_CITY_DRIVING Lane_LaneType = 2
	// Lane_BIKING       Lane_LaneType = 3
	// Lane_SIDEWALK     Lane_LaneType = 4
	// Lane_PARKING      Lane_LaneType = 5
	// Lane_SHOULDER     Lane_LaneType = 6

	// Lane_WAITING

	//262144 公交车道
	//524288 自行车道
	//134217728 代转车道
	if ltype == 524288 {
		return hdmap.Lane_BIKING.Enum()
	}
	return hdmap.Lane_CITY_DRIVING.Enum()
}

func UpdateSharedBoundary(minAzimuth, maxAzimuth float64) error {
	//需要处理公用边界的情况
	sqlStr := `UPDATE aodx_lane_boundary SET bgeom = ST_Reverse(bgeom)
	WHERE abs(azimuth(startpoint(bgeom), endpoint(bgeom)) - azimuth(startpoint(geom), endpoint(geom))) > ?
		  and abs(azimuth(startpoint(bgeom), endpoint(bgeom)) - azimuth(startpoint(geom), endpoint(geom))) < ?;`
	_, err := db.Exec(sqlStr, minAzimuth, maxAzimuth)
	if err != nil {
		return err
	}
	return nil
}

func HasMutiStopLine(lanepid, signalpid string) string {
	return "" //暂时不处理
	//需要处理公用边界的情况
	sqlStr := `select object_pid
				from ibd_object_lane_link_rel
				where object_type = 7
					and lane_link_pid = ?
					and (
						SELECT count(*)
						FROM IBD_OBJECT_COMBINANT
						WHERE object_type = 7
							and combinant_pid in (
								SELECT combinant_pid
								FROM IBD_OBJECT_COMBINANT
								WHERE object_type = 17
									and object_pid = ?
							)
					) > 1;`

	rs := db.QueryRow(sqlStr, lanepid, signalpid)
	var stpid string
	if err := rs.Scan(&stpid); err != nil {
		if err == sql.ErrNoRows {
			return ""
		}
		log.Errorf("判断查询多停止线的信号灯(pid:%s)出错, 详情: %v", signalpid, err)
		return ""
	}
	return stpid
}
